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

Compare commits

...

53 Commits

Author SHA1 Message Date
dudaodong
3f8e306ced doc: update deprecated warning text 2024-08-08 15:44:19 +08:00
dudaodong
0b29f0520d feat: add Debounce function 2024-08-08 15:19:38 +08:00
dudaodong
8611ec0c10 feat: add UniqueByComparator 2024-08-08 11:23:11 +08:00
dudaodong
286e10d189 refactoring: memory optimization for unique slice 2024-08-08 10:51:33 +08:00
dudaodong
3e7f94b03e fix: fix UniqueBy bug 2024-08-08 10:40:23 +08:00
dudaodong
356351896d feat: add UniqueByParallel for slice 2024-08-08 10:20:52 +08:00
dudaodong
9be124211e refact: preallocate in Merge map 2024-08-01 11:00:24 +08:00
dudaodong
f467658481 Merge branch 'rc' into v2 2024-07-31 10:50:29 +08:00
zyfx
5c9d0e396e Update condition.go (#234) 2024-07-31 10:16:03 +08:00
dudaodong
8f74460c1b fix: remove scientific notation in DecimalBytes 2024-07-30 14:17:17 +08:00
dudaodong
d5752499bf fix: remove scientific notation in DecimalBytes 2024-07-29 11:42:56 +08:00
dudaodong
eb7cf76eae doc: fix doc error 2024-07-18 11:44:00 +08:00
dudaodong
4af074d181 doc: add play ground demo 2024-07-18 11:34:16 +08:00
dudaodong
73fb8fefd2 release v2.3.2 2024-07-18 10:52:54 +08:00
dudaodong
9cf535055d doc: update readme file 2024-07-18 10:50:29 +08:00
dudaodong
8be7b3e396 rename UnwrapOr 2024-07-18 10:12:16 +08:00
梧桐
dac706d700 🐛 Fixing a bug. about pointer package function (#230)
update:
1. UnwarpOr
2. UnwarpOrDefault
3. ExtractPointer

add:
1. UnwrapOr
2. IsNil
2024-07-18 10:05:07 +08:00
dudaodong
2097277a7d doc: add doc for UniqueByField 2024-06-25 15:20:03 +08:00
dudaodong
95b516e278 feat: add UniqueByField 2024-06-24 19:36:02 +08:00
dudaodong
ca373b00a7 feat: add GetOrSet 2024-06-24 17:29:12 +08:00
dudaodong
a220220f09 feat: add GetOrSet 2024-06-24 17:28:51 +08:00
dudaodong
aeef0418a4 fix: fix bug of CopyDir 2024-06-24 17:09:31 +08:00
dudaodong
9b7d8d7abf doc: update vitepress version 2024-06-11 10:06:33 +08:00
dudaodong
4d21e81263 feat: add Timeout config for http client 2024-06-04 16:28:00 +08:00
残念
ce2397422e perf(slice): make a clearer panic description (#223) 2024-05-30 16:55:30 +08:00
Cannian
4b3a62b36a perf(validator): check Ipv4、Ipv6 by more graceful method (#220) 2024-05-25 08:58:33 +08:00
dudaodong
e054680d20 doc: ignoreDeadLinks in doc 2024-05-14 11:42:48 +08:00
dudaodong
5381842eec doc: update doc for v2.3.1 2024-05-14 11:26:14 +08:00
dudaodong
6e0498514c doc: update doc for v2.3.1 2024-05-14 11:25:01 +08:00
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
62 changed files with 3417 additions and 893 deletions

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.3.0-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.2-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)
@@ -660,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)]
@@ -731,8 +734,10 @@ import "github.com/duke-git/lancet/v2/fileutil"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ReadFile)]
- **<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)]
[[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>
@@ -795,20 +800,28 @@ import "github.com/duke-git/lancet/v2/function"
[[play](https://go.dev/play/p/hbON-Xeyn5N)]
- **<big>Pipeline</big>** : takes a list of functions and returns a function whose param will be passed into the functions one by one.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Pipeline)]
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
- **<big>AcceptIf</big>** : returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#AcceptIf)]
[[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)]
@@ -888,6 +901,18 @@ 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>GetOrSet</big>** : returns value of the given key or set the given value value if not present.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#GetOrSet)]
[[play](https://go.dev/play/p/IVQwO1OkEJC)]
- **<big>MapToStruct</big>** : converts map to struct.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapToStruct)]
[[play](https://go.dev/play/p/7wYyVfX38Dp)]
- **<big>ToSortedSlicesDefault</big>** : converts a map to two slices sorted by key: one for the keys and another for the values.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesDefault)]
[[play](https://go.dev/play/p/43gEM2po-qy)]
- **<big>ToSortedSlicesWithComparator</big>** : converts a map to two slices sorted by key and using a custom comparison function: one for the keys and another for the values.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesWithComparator)]
[[play](https://go.dev/play/p/0nlPo6YLdt3)]
- **<big>NewConcurrentMap</big>** : creates a ConcurrentMap with specific shard count.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewConcurrentMap)]
[[play](https://go.dev/play/p/3PenTPETJT0)]
@@ -959,12 +984,16 @@ import "github.com/duke-git/lancet/v2/mathutil"
[[play](https://go.dev/play/p/aumarSHIGzP)]
- **<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)]
@@ -1006,6 +1035,7 @@ import "github.com/duke-git/lancet/v2/mathutil"
[[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>
@@ -1098,7 +1128,7 @@ import "github.com/duke-git/lancet/v2/pointer"
- **<big>Unwrap</big>** : return the value from the pointer.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#Unwrap)]
[[play](https://go.dev/play/p/cgeu3g7cjWb)]
- **<big>UnwarpOr</big>** : UnwarpOr returns the value from the pointer or fallback if the pointer is nil.
- **<big>UnwrapOr</big>** : UnwrapOr returns the value from the pointer or fallback if the pointer is nil.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#UnwrapOr)]
[[play](https://go.dev/play/p/mmNaLC38W8C)]
- **<big>UnwarpOrDefault</big>** : UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
@@ -1180,11 +1210,13 @@ import "github.com/duke-git/lancet/v2/retry"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#BackoffStrategy)]
- **<big>RetryWithCustomBackoff</big>** : set abitary custom backoff strategy.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithCustomBackoff)]
[[play](https://go.dev/play/p/jIm_o2vb5Y4)]
- **<big>RetryWithLinearBackoff</big>** : set linear strategy backoff.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithLinearBackoff)]
[[play](https://go.dev/play/p/PDet2ZQZwcB)]
- **<big>RetryWithExponentialWithJitterBackoff</big>** : set exponential strategy backoff.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
[[play](https://go.dev/play/p/xp1avQmn16X)]
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1384,9 +1416,12 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Unique</big>** : remove duplicate elements in slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Unique)]
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
- **<big>UniqueBy</big>** : call iteratee func with every item of slice, then remove duplicated.
- **<big>UniqueBy</big>** : remove duplicate elements from the input slice based on the values returned by the iteratee function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueBy)]
[[play](https://go.dev/play/p/UR323iZLDpv)]
- **<big>UniqueByField</big>** : remove duplicate elements in struct slice by struct field.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByField)]
[[play](https://go.dev/play/p/6cifcZSPIGu)]
- **<big>Union</big>** : creates a slice of unique elements, in order, from all given slices.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Union)]
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
@@ -1413,6 +1448,15 @@ import "github.com/duke-git/lancet/v2/slice"
[[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)]
[[play](https://go.dev/play/p/0_2rlLEMBXL)]
- **<big>LeftPadding</big>** : adds padding to the left begin of a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#LeftPadding)]
[[play](https://go.dev/play/p/jlQVoelLl2k)]
<h3 id="stream"> 19. Stream package implements a sequence of elements supporting sequential and operations. this package is an experiment to explore if stream in go can work as the way java does. its function is very limited. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1660,8 +1704,11 @@ import "github.com/duke-git/lancet/v2/strutil"
[[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>
@@ -2001,7 +2048,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.3.0-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.2-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)
@@ -659,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)]
@@ -730,8 +733,10 @@ import "github.com/duke-git/lancet/v2/fileutil"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFile)]
- **<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)]
@@ -799,22 +804,31 @@ import "github.com/duke-git/lancet/v2/function"
[[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
@@ -889,6 +903,18 @@ 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>GetOrSet</big>** : 返回给定键的值,如果不存在则设置该值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrSet)]
[[play](https://go.dev/play/p/IVQwO1OkEJC)]
- **<big>MapToStruct</big>** : 将map转成struct。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#MapToStruct)]
[[play](https://go.dev/play/p/7wYyVfX38Dp)]
- **<big>ToSortedSlicesDefault</big>** : 将map的key和value转化成两个根据key的值从小到大排序的切片value切片中元素的位置与key对应。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesDefault)]
[[play](https://go.dev/play/p/43gEM2po-qy)]
- **<big>ToSortedSlicesWithComparator</big>** : 将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片value切片中元素的位置与key对应。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesWithComparator)]
[[play](https://go.dev/play/p/0nlPo6YLdt3)]
- **<big>NewConcurrentMap</big>** : ConcurrentMap 协程安全的 map 结构。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewConcurrentMap)]
[[play](https://go.dev/play/p/3PenTPETJT0)]
@@ -960,12 +986,16 @@ import "github.com/duke-git/lancet/v2/mathutil"
[[play](https://go.dev/play/p/aumarSHIGzP)]
- **<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)]
@@ -1007,7 +1037,7 @@ import "github.com/duke-git/lancet/v2/mathutil"
[[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>
@@ -1180,10 +1210,13 @@ import "github.com/duke-git/lancet/v2/retry"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#BackoffStrategy)]
- **<big>RetryWithCustomBackoff</big>** : 设置自定义退避策略。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithCustomBackoff)]
[[play](https://go.dev/play/p/jIm_o2vb5Y4)]
- **<big>RetryWithLinearBackoff</big>** : 设置线性策略退避。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithLinearBackoff)]
[[play](https://go.dev/play/p/PDet2ZQZwcB)]
- **<big>RetryWithExponentialWithJitterBackoff</big>** : 设置指数策略退避。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
[[play](https://go.dev/play/p/xp1avQmn16X)]
@@ -1384,9 +1417,12 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Unique</big>** : 删除切片中的重复元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Unique)]
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
- **<big>UniqueBy</big>** : 对切片的每个元素调用 iteratee 函数,然后删除重复元素
- **<big>UniqueBy</big>** : 根据迭代函数返回的值,从输入切片中移除重复元素。此函数保持元素的顺序
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueBy)]
[[play](https://go.dev/play/p/UR323iZLDpv)]
- **<big>UniqueByField</big>** : 根据struct字段对struct切片去重复。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByField)]
[[play](https://go.dev/play/p/6cifcZSPIGu)]
- **<big>Union</big>** : 合并多个切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Union)]
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
@@ -1412,6 +1448,15 @@ import "github.com/duke-git/lancet/v2/slice"
[[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)]
[[play](https://go.dev/play/p/0_2rlLEMBXL)]
- **<big>LeftPadding</big>** : 在切片的左部添加元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#LeftPadding)]
[[play](https://go.dev/play/p/jlQVoelLl2k)]
@@ -1502,6 +1547,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
@@ -1663,8 +1709,11 @@ import "github.com/duke-git/lancet/v2/strutil"
[[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

@@ -49,9 +49,7 @@ func Or[T, U any](a T, b U) bool {
// Xor returns true if a or b but not both is truthy.
// Play: https://go.dev/play/p/gObZrW7ZbG8
func Xor[T, U any](a T, b U) bool {
valA := Bool(a)
valB := Bool(b)
return (valA || valB) && valA != valB
return Bool(a) != Bool(b)
}
// Nor returns true if neither a nor b is truthy.
@@ -63,9 +61,7 @@ func Nor[T, U any](a T, b U) bool {
// Xnor returns true if both a and b or neither a nor b are truthy.
// Play: https://go.dev/play/p/OuDB9g51643
func Xnor[T, U any](a T, b U) bool {
valA := Bool(a)
valB := Bool(b)
return (valA && valB) || (!valA && !valB)
return Bool(a) == Bool(b)
}
// Nand returns false if both a and b are truthy.

View File

@@ -397,6 +397,7 @@ func GbkToUtf8(bs []byte) ([]byte, error) {
}
// 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 ""
@@ -418,6 +419,7 @@ func ToStdBase64(value any) string {
}
// 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 ""
@@ -439,6 +441,7 @@ func ToUrlBase64(value any) string {
}
// 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 ""
@@ -460,6 +463,7 @@ func ToRawStdBase64(value any) string {
}
// 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 ""

View File

@@ -187,10 +187,11 @@ func (s Set[T]) EachWithBreak(iteratee func(item T) bool) {
// Pop delete the top element of set then return it, if set is empty, return nil-value of T and false.
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

View File

@@ -243,25 +243,34 @@ 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()

View File

@@ -40,6 +40,7 @@ export const slugify = (str: string): string =>
export const commonConfig = defineConfig({
title: 'Lancet',
appearance: true,
ignoreDeadLinks: true,
markdown: {
theme: {

View File

@@ -891,7 +891,7 @@ func main() {
func ToStdBase64(value any) string
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/_fLJqJD3NMo)</span></b>
```go
package main
@@ -963,7 +963,7 @@ func main() {
func ToUrlBase64(value any) string
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/C_d0GlvEeUR)</span></b>
```go
package main
@@ -1032,7 +1032,7 @@ func main() {
func ToRawStdBase64(value any) string
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wSAr3sfkDcv)</span></b>
```go
package main
@@ -1064,7 +1064,7 @@ func main() {
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToRawStdBase64(floatVal)
afterEncode := convertor.ToRawStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
@@ -1090,7 +1090,7 @@ func main() {
<p>值转换为 ToRawUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
<b>函数签名:</b>
<b>函数签名:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/HwdDPFcza1O)</span></b>
```go
func ToRawUrlBase64(value any) string
@@ -1109,7 +1109,7 @@ import (
func main() {
stringVal := "hello"
afterEncode = convertor.ToRawUrlBase64(stringVal)
afterEncode := convertor.ToRawUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
@@ -1132,11 +1132,11 @@ func main() {
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToRawStdBase64(boolVal)
afterEncode = convertor.ToRawUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawStdBase64(errVal)
afterEncode = convertor.ToRawUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:

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

@@ -24,7 +24,7 @@ import (
- [New](#New)
- [FromSlice](#FromSlice)
- [Values](#Values)
- [Values<sup>deprecated</sup>](#Values)
- [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
- [AddIfNotExistBy](#AddIfNotExistBy)
@@ -101,10 +101,11 @@ func main() {
}
```
### <span id="Values">Values<sup>deprecated</sup></span>
### <span id="Values">Values</span>
<p>获取集合中所有元素的切片<br>
<a href='#ToSlice'>ToSlice()</a> 方法提供与 Values 方法相同的功能</p>
<p>获取集合中所有元素的切片。</p>
> ⚠️ 本函数已弃用,使用`ToSlice`代替。
<b>函数签名:</b>

View File

@@ -976,7 +976,7 @@ func main() {
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/r0hPmKWhsgf)</span></b>
```go
package main
@@ -1035,7 +1035,7 @@ func main() {
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/teMXnCsdSEw)</span></b>
```go
package main

View File

@@ -28,7 +28,8 @@ import (
- [Before](#Before)
- [CurryFn](#CurryFn)
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Debounce](#Debounce)
- [Debounced<sup>deprecated</sup>](#Debounced)
- [Delay](#Delay)
- [Schedule](#Schedule)
- [Pipeline](#Pipeline)
@@ -193,9 +194,58 @@ func main() {
}
```
### <span id="Debounce">Debounce</span>
<p>创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。</p>
<b>函数签名:</b>
```go
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
```
### <span id="Debounced">Debounced</span>
<p>创建一个debounced函数该函数延迟调用fn直到自上次调用debounced函数后等待持续时间过去。</p>
<p>创建一个函数的去抖动版本。</p>
> ⚠️ 本函数已弃用. 使用 `Debounce` 代替.
<b>函数签名:</b>
@@ -424,7 +474,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
@@ -461,7 +511,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
@@ -496,7 +546,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
@@ -536,7 +586,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
@@ -578,7 +628,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
@@ -615,7 +665,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
@@ -652,7 +702,7 @@ func main() {
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/XlXHHtzCf7d)</span></b>
```go
package main
@@ -663,9 +713,8 @@ import (
)
func main() {
adder := AcceptIf(
And(
adder := function.AcceptIf(
function.And(
func(x int) bool {
return x > 10
}, func(x int) bool {

View File

@@ -44,6 +44,9 @@ import (
- [Minus](#Minus)
- [IsDisjoint](#IsDisjoint)
- [HasKey](#HasKey)
- [MapToStruct](#MapToStruct)
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
- [NewConcurrentMap](#NewConcurrentMap)
- [ConcurrentMap_Get](#ConcurrentMap_Get)
- [ConcurrentMap_Set](#ConcurrentMap_Set)
@@ -52,6 +55,8 @@ import (
- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete)
- [ConcurrentMap_Has](#ConcurrentMap_Has)
- [ConcurrentMap_Range](#ConcurrentMap_Range)
- [GetOrSet](#GetOrSet)
<div STYLE="page-break-after: always;"></div>
@@ -946,14 +951,6 @@ func main() {
<p>检查map是否包含某个key。用于代替以下样板代码:</p>
```go
_, haskey := amap["baz"];
if haskey {
fmt.Println("map has key baz")
}
```
<b>函数签名:</b>
```go
@@ -988,6 +985,141 @@ func main() {
}
```
### <span id="MapToStruct">MapToStruct</span>
<p>将map转成struct。</p>
<b>函数签名:</b>
```go
func MapToStruct(m map[string]any, structObj any) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7wYyVfX38Dp)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
personReqMap := map[string]any{
"name": "Nothin",
"max_age": 35,
"page": 1,
"pageSize": 10,
}
type PersonReq struct {
Name string `json:"name"`
MaxAge int `json:"max_age"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
var personReq PersonReq
_ = maputil.MapToStruct(personReqMap, &personReq)
fmt.Println(personReq)
// Output:
// {Nothin 35 1 10}
}
```
### <span id="ToSortedSlicesDefault">ToSortedSlicesDefault</span>
<p>将map的key和value转化成两个根据key的值从小到大排序的切片value切片中元素的位置与key对应。</p>
<b>函数签名:</b>
```go
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/43gEM2po-qy)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys, values := ToSortedSlicesDefault(m)
fmt.Println(keys)
fmt.Println(values)
// Output:
// [1 2 3]
// [a b c]
}
```
### <span id="ToSortedSlicesWithComparator">ToSortedSlicesWithComparator</span>
<p>将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片value切片中元素的位置与key对应。</p>
<b>函数签名:</b>
```go
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/0nlPo6YLdt3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m1 := map[time.Time]string{
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
}
keys1, values1 := maputil.ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
return a.Before(b)
})
m2 := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys2, values2 := maputil.ToSortedSlicesWithComparator(m2, func(a, b int) bool {
return a > b
})
fmt.Println(keys2)
fmt.Println(values2)
fmt.Println(keys1)
fmt.Println(values1)
// Output:
// [3 2 1]
// [c b a]
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
// [yesterday today tomorrow]
}
```
### <span id="NewConcurrentMap">NewConcurrentMap</span>
<p>ConcurrentMap协程安全的map结构。</p>
@@ -1050,15 +1182,15 @@ func main() {
wg1.Wait()
var wg2 sync.WaitGroup
wg2.Add(5)
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
val, ok := cm.Get(fmt.Sprintf("%d", n))
fmt.Println(val, ok)
wg2.Done()
wg2.Done()
}(j)
}
wg2.Wait()
wg2.Wait()
// output: (order may change)
// 1 true
@@ -1104,15 +1236,15 @@ func main() {
wg1.Wait()
var wg2 sync.WaitGroup
wg2.Add(5)
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
val, ok := cm.Get(fmt.Sprintf("%d", n))
fmt.Println(val, ok)
wg2.Done()
wg2.Done()
}(j)
}
wg2.Wait()
wg2.Wait()
// output: (order may change)
// 1 true
@@ -1202,7 +1334,7 @@ func main() {
wg1.Wait()
var wg2 sync.WaitGroup
wg2.Add(5)
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
cm.Delete(fmt.Sprintf("%d", n))
@@ -1248,7 +1380,7 @@ func main() {
wg1.Wait()
var wg2 sync.WaitGroup
wg2.Add(5)
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n))
@@ -1298,7 +1430,7 @@ func main() {
wg1.Wait()
var wg2 sync.WaitGroup
wg2.Add(5)
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
@@ -1353,3 +1485,41 @@ func main() {
})
}
```
### <span id="GetOrSet">GetOrSet</span>
<p>返回给定键的值,如果不存在则设置该值。</p>
<b>函数签名:</b>
```go
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/IVQwO1OkEJC)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
1: "a",
}
result1 := maputil.GetOrSet(m, 1, "1")
result2 := maputil.GetOrSet(m, 2, "b")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// a
// b
}
```

View File

@@ -508,7 +508,7 @@ func main() {
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/8hOeSADZPCo)</span></b>
```go
package main
@@ -544,7 +544,7 @@ func main() {
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wy5bYEyUKKG)</span></b>
```go
package main
@@ -577,10 +577,10 @@ func main() {
<b>函数签名:</b>
```go
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vbCBrQHZEED)</span></b>
```go
package main
@@ -613,10 +613,10 @@ func main() {
<b>函数签名:</b>
```go
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Qk9KPd2IdDb)</span></b>
```go
package main
@@ -1130,7 +1130,7 @@ func main() {
### <span id="Div">Div</span>
<p>除法运算.</p>
<p>除法运算</p>
<b>函数签名:</b>
@@ -1138,7 +1138,7 @@ func main() {
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/WLxDdGXXYat)</span></b>
```go
package main
@@ -1156,6 +1156,7 @@ func main() {
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 2.25
// 0.5

View File

@@ -624,7 +624,9 @@ func main() {
### <span id="HttpGet">HttpGet</span>
<p>发送http get请求。(已废弃:使用SendRequest)</p>
<p>发送http get请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -666,7 +668,9 @@ func main() {
### <span id="HttpPost">HttpPost</span>
<p>发送http post请求。(已废弃:使用SendRequest)</p>
<p>发送http post请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -713,7 +717,9 @@ func main() {
### <span id="HttpPut">HttpPut</span>
<p>发送http put请求。(已废弃:使用SendRequest)</p>
<p>发送http put请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -763,7 +769,9 @@ func main() {
### <span id="HttpDelete">HttpDelete</span>
<p>发送http delete请求。(已废弃:使用SendRequest)</p>
<p>发送http delete请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -802,7 +810,9 @@ func main() {
### <span id="HttpPatch">HttpPatch</span>
<p>发送http patch请求。(已废弃:使用SendRequest)</p>
<p>发送http patch请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>

View File

@@ -25,8 +25,8 @@ import (
- [Of](#Of)
- [Unwrap](#Unwrap)
- [ExtractPointer](#ExtractPointer)
- [UnwarpOr](#UnwarpOr)
- [UnwarpOrDefault](#UnwarpOrDefault)
- [UnwrapOr](#UnwrapOr)
- [UnwrapOrDefault](#UnwrapOrDefault)
<div STYLE="page-break-after: always;"></div>
@@ -136,14 +136,14 @@ func main() {
}
```
### <span id="UnwarpOr">UnwarpOr</span>
### <span id="UnwrapOr">UnwrapOr</span>
<p>返回指针的值如果指针为零值则返回fallback。</p>
<b>函数签名:</b>
```go
UnwarpOr[T any](p *T, fallback T) T
UnwrapOr[T any](p *T, fallback T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/mmNaLC38W8C)</span></b>
@@ -163,10 +163,10 @@ func main() {
var c *int
var d *string
result1 := pointer.UnwarpOr(&a, 456)
result2 := pointer.UnwarpOr(&b, "abc")
result3 := pointer.UnwarpOr(c, 456)
result4 := pointer.UnwarpOr(d, "def")
result1 := pointer.UnwrapOr(&a, 456)
result2 := pointer.UnwrapOr(&b, "abc")
result3 := pointer.UnwrapOr(c, 456)
result4 := pointer.UnwrapOr(d, "def")
fmt.Println(result1)
fmt.Println(result2)
@@ -181,14 +181,14 @@ func main() {
}
```
### <span id="UnwarpOrDefault">UnwarpOrDefault</span>
### <span id="UnwrapOrDefault">UnwrapOrDefault</span>
<p>返回指针的值,如果指针为零值,则返回相应零值。</p>
<b>函数签名:</b>
```go
UnwarpOrDefault[T any](p *T) T
UnwrapOrDefault[T any](p *T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
@@ -208,10 +208,10 @@ func main() {
var c *int
var d *string
result1 := pointer.UnwarpOrDefault(&a)
result2 := pointer.UnwarpOrDefault(&b)
result3 := pointer.UnwarpOrDefault(c)
result4 := pointer.UnwarpOrDefault(d)
result1 := pointer.UnwrapOrDefault(&a)
result2 := pointer.UnwrapOrDefault(&b)
result3 := pointer.UnwrapOrDefault(c)
result4 := pointer.UnwrapOrDefault(d)
fmt.Println(result1)
fmt.Println(result2)

View File

@@ -331,7 +331,7 @@ func main() {
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jIm_o2vb5Y4)</span></b>
```go
package main
@@ -384,7 +384,7 @@ func main() {
func RetryWithLinearBackoff(interval time.Duration) Option
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/PDet2ZQZwcB)</span></b>
```go
package main
@@ -429,7 +429,7 @@ func main() {
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/xp1avQmn16X)</span></b>
```go
package main

View File

@@ -86,6 +86,7 @@ import (
- [ToSlicePointer](#ToSlicePointer)
- [Unique](#Unique)
- [UniqueBy](#UniqueBy)
- [UniqueByField](#UniqueByField)
- [Union](#Union)
- [UnionBy](#UnionBy)
- [UpdateAt](#UpdateAt)
@@ -94,6 +95,9 @@ import (
- [Join](#Join)
- [Partition](#Partition)
- [SetToDefaultIf](#SetToDefaultIf)
- [Break](#Break)
- [RightPadding](#RightPadding)
- [LeftPadding](#LeftPadding)
<div STYLE="page-break-after: always;"></div>
@@ -319,12 +323,12 @@ func main() {
### <span id="Concat">Concat</span>
<p>合并多个slices到slice中</p>
<p>创建一个新的切片,将传入的切片拼接起来返回。</p>
<b>函数签名:</b>
```go
func Concat[T any](slice []T, slices ...[]T) []T
func Concat[T any](slices ...[]T) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
@@ -893,10 +897,12 @@ func main() {
}
```
### <span id="Find">Find (废弃:使用 FindBy)</span>
### <span id="Find">Find</span>
<p>遍历slice的元素返回第一个通过predicate函数真值测试的元素</p>
> ⚠️ 本函数已弃用,使用`FindBy`代替。
<b>函数签名:</b>
```go
@@ -965,10 +971,12 @@ func main() {
}
```
### <span id="FindLast">FindLast(废弃:使用 FindLastBy)</span>
### <span id="FindLast">FindLast</span>
<p>遍历slice的元素返回最后一个通过predicate函数真值测试的元素。</p>
> ⚠️ 本函数已弃用,使用`FindLastBy`代替。
<b>函数签名:</b>
```go
@@ -1240,10 +1248,12 @@ func main() {
}
```
### <span id="IntSlice">IntSlice (已弃用: 使用 go1.18+泛型代替)</span>
### <span id="IntSlice">IntSlice</span>
<p>将接口切片转换为int切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -1269,10 +1279,12 @@ func main() {
}
```
### <span id="InterfaceSlice">InterfaceSlice(已弃用: 使用 go1.18+泛型代替)</span>
### <span id="InterfaceSlice">InterfaceSlice</span>
<p>将值转换为接口切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -1543,6 +1555,8 @@ func main() {
<p>合并多个切片(不会消除重复元素).</p>
> ⚠️ 本函数已弃用,使用`Concat`代替。
<b>函数签名:</b>
```go
@@ -1602,7 +1616,9 @@ func main() {
### <span id="Reduce">Reduce</span>
<p>将切片中的元素依次运行iteratee函数返回运行结果(废弃建议使用ReduceBy)</p>
<p>将切片中的元素依次运行iteratee函数返回运行结果</p>
> ⚠️ 本函数已弃用,使用`ReduceBy`代替。
<b>函数签名:</b>
@@ -2128,10 +2144,12 @@ func main() {
}
```
### <span id="StringSlice">StringSlice(已弃用: 使用 go1.18+泛型代替)</span>
### <span id="StringSlice">StringSlice</span>
<p>将接口切片转换为字符串切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -2280,12 +2298,12 @@ func main() {
### <span id="UniqueBy">UniqueBy</span>
<p>对切片的每个元素调用iteratee函数然后删除重复元素</p>
<p>根据迭代函数返回的值,从输入切片中移除重复元素。此函数保持元素的顺序。</p>
<b>函数签名:</b>
```go
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/UR323iZLDpv)</span></b>
@@ -2305,7 +2323,48 @@ func main() {
fmt.Println(result)
// Output:
// [1 2 0]
// [1 2 3]
}
```
### <span id="UniqueByField">UniqueByField</span>
<p>根据struct字段对struct切片去重复。</p>
<b>函数签名:</b>
```go
func UniqueByField[T any](slice []T, field string) ([]T, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/6cifcZSPIGu)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
users := []User{
{ID: 1, Name: "a"},
{ID: 2, Name: "b"},
{ID: 1, Name: "c"},
}
result, err := slice.UniqueByField(users, "ID")
if err != nil {
}
fmt.Println(result)
// Output:
// [{1 a} {2 b}]
}
```
@@ -2581,7 +2640,7 @@ func main() {
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9AXGlPRC0-A)</span></b>
```go
import (
@@ -2591,13 +2650,100 @@ import (
func main() {
strs := []string{"a", "b", "a", "c", "d", "a"}
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
fmt.Println(modifiedStrs)
fmt.Println(count)
fmt.Println(count)
// Output:
// [ b c d ]
// 3
// [ 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>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/yLYcBTyeQIz)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
even := func(n int) bool { return n%2 == 0 }
resultEven, resultAfterFirstEven := slice.Break(nums, even)
fmt.Println(resultEven)
fmt.Println(resultAfterFirstEven)
// Output:
// [1]
// [2 3 4 5]
}
```
<span id="RightPadding">RightPadding</span>
<p>在切片的右部添加元素。</p>
<b>函数签名:</b>
```go
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/0_2rlLEMBXL)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
}
```
<span id="LeftPadding">LeftPadding</span>
<p>在切片的左部添加元素。</p>
<b>函数签名:</b>
```go
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jlQVoelLl2k)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
```

View File

@@ -62,6 +62,7 @@ import (
- [RemoveWhiteSpace](#RemoveWhiteSpace)
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
<div STYLE="page-break-after: always;"></div>
@@ -1475,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 (
@@ -1505,10 +1506,10 @@ func main() {
<b>函数签名:</b>
```go
HammingDistance(a, b string) (int, error)
func HammingDistance(a, b string) (int, error)
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/glNdQEA9HUi)</span></b>
```go
import (
@@ -1528,4 +1529,38 @@ func main() {
// 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

@@ -571,6 +571,7 @@ func main() {
}
```
### <span id="EncodeByte">EncodeByte</span>
<p>Encode data to byte slice.</p>
@@ -610,7 +611,7 @@ func main() {
func DecodeByte(data []byte, target any) error
```
<b>Example:<span style="float:right;display:inline-block;"></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zI6xsmuQRbn)</span></b>
```go
package main
@@ -636,69 +637,6 @@ func main() {
}
```
### <span id="DeepClone">DeepClone</span>
<p>Creates a deep copy of passed item, can't clone unexported field of struct.</p>
<b>Signature:</b>
```go
func DeepClone[T any](src T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/j4DP5dquxnk)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
type Struct struct {
Str string
Int int
Float float64
Bool bool
Nil interface{}
unexported string
}
cases := []interface{}{
true,
1,
0.1,
map[string]int{
"a": 1,
"b": 2,
},
&Struct{
Str: "test",
Int: 1,
Float: 0.1,
Bool: true,
Nil: nil,
// unexported: "can't be cloned",
},
}
for _, item := range cases {
cloned := convertor.DeepClone(item)
isPointerEqual := &cloned == &item
fmt.Println(cloned, isPointerEqual)
}
// Output:
// true false
// 1 false
// 0.1 false
// map[a:1 b:2] false
// &{test 1 0.1 true <nil> } false
}
```
### <span id="CopyProperties">CopyProperties</span>
@@ -779,41 +717,6 @@ func main() {
}
```
### <span id="ToInterface">ToInterface</span>
<p>Converts reflect value to its interface type.</p>
<b>Signature:</b>
```go
func ToInterface(v reflect.Value) (value interface{}, ok bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/syqw0-WG7Xd)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
val := reflect.ValueOf("abc")
iVal, ok := convertor.ToInterface(val)
fmt.Printf("%T\n", iVal)
fmt.Printf("%v\n", iVal)
fmt.Println(ok)
// Output:
// string
// abc
// true
}
```
### <span id="Utf8ToGbk">Utf8ToGbk</span>
<p>Converts utf8 encoding data to GBK encoding data.</p>
@@ -883,7 +786,7 @@ func main() {
### <span id="ToStdBase64">ToStdBase64</span>
<p>Convert a value to a string encoded in standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
<p>Convert a value to a string encoded in standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
<b>Signature:</b>
@@ -891,7 +794,7 @@ func main() {
func ToStdBase64(value any) string
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/_fLJqJD3NMo)</span></b>
```go
package main
@@ -953,9 +856,11 @@ func main() {
```
### <span id="ToUrlBase64">ToUrlBase64</span>
<p>Convert a value to a string encoded in url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
<p>Convert a value to a string encoded in url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
<b>Signature:</b>
@@ -963,7 +868,7 @@ func main() {
func ToUrlBase64(value any) string
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/C_d0GlvEeUR)</span></b>
```go
package main
@@ -1024,7 +929,7 @@ func main() {
### <span id="ToRawStdBase64">ToRawStdBase64</span>
<p>Convert a value to a string encoded in raw standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
<p>Convert a value to a string encoded in raw standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
<b>Signature:</b>
@@ -1032,7 +937,7 @@ func main() {
func ToRawStdBase64(value any) string
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wSAr3sfkDcv)</span></b>
```go
package main
@@ -1045,7 +950,7 @@ import (
func main() {
stringVal := "hello"
afterEncode = convertor.ToRawStdBase64(stringVal)
afterEncode := convertor.ToRawStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
@@ -1088,7 +993,7 @@ func main() {
### <span id="ToRawUrlBase64">ToRawUrlBase64</span>
<p> Convert a value to a string encoded in raw url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
<p> Convert a value to a string encoded in raw url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
<b>Signature:</b>
@@ -1096,7 +1001,7 @@ func main() {
func ToRawUrlBase64(value any) string
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/HwdDPFcza1O)</span></b>
```go
package main
@@ -1109,7 +1014,7 @@ import (
func main() {
stringVal := "hello"
afterEncode = convertor.ToRawUrlBase64(stringVal)
afterEncode := convertor.ToRawUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
@@ -1132,11 +1037,11 @@ func main() {
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToRawStdBase64(boolVal)
afterEncode = convertor.ToRawUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawStdBase64(errVal)
afterEncode = convertor.ToRawUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
@@ -1148,4 +1053,67 @@ func main() {
// 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

@@ -24,7 +24,7 @@ import (
- [New](#New)
- [FromSlice](#FromSlice)
- [Values](#Values)
- [Values<sup>deprecated</sup>](#Values)
- [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
- [AddIfNotExistBy](#AddIfNotExistBy)
@@ -102,10 +102,11 @@ func main() {
}
```
### <span id="Values">Values<sup>deprecated</sup></span>
### <span id="Values">Values</span>
<p>Return slice of all set data.<br>
The <a href='#ToSlice'>ToSlice()</a> function provides the same functionality as Values and returns a slice containing all values of the set.</p>
<p>Return slice of all set data.</p>
> ⚠️ This function is deprecated. use `ToSlice` instead.
<b>Signature:</b>

View File

@@ -167,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>
@@ -974,7 +974,7 @@ func main() {
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/r0hPmKWhsgf)</span></b>
```go
package main
@@ -1033,7 +1033,7 @@ func main() {
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/teMXnCsdSEw)</span></b>
```go
package main

View File

@@ -28,7 +28,8 @@ import (
- [Before](#Before)
- [CurryFn](#CurryFn)
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Debounce](#Debounce)
- [Debounced<sup>deprecated</sup>](#Debounced)
- [Delay](#Delay)
- [Schedule](#Schedule)
- [Pipeline](#Pipeline)
@@ -191,11 +192,59 @@ func main() {
// ABCDE
}
```
### <span id="Debounce">Debounce</span>
<p>Creates a debounced version of the provided function. The debounced function will only invoke the original function after the specified delay has passed since the last time it was invoked. It also supports canceling the debounce.</p>
<b>Signature:</b>
```go
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
```
### <span id="Debounced">Debounced</span>
<p>Creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.</p>
> ⚠️ This function is deprecated. use `Debounce` instead.
<b>Signature:</b>
```go
@@ -423,7 +472,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
@@ -460,7 +509,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
@@ -495,7 +544,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
@@ -535,7 +584,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
@@ -577,7 +626,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
@@ -614,7 +663,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
@@ -643,9 +692,7 @@ func main() {
### <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>
<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>
@@ -653,7 +700,7 @@ An apply function that also takes an argument of type T and returns a modified v
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/XlXHHtzCf7d)</span></b>
```go
package main
@@ -665,8 +712,8 @@ import (
func main() {
adder := AcceptIf(
And(
adder := function.AcceptIf(
function.And(
func(x int) bool {
return x > 10
}, func(x int) bool {

View File

@@ -44,6 +44,9 @@ import (
- [Minus](#Minus)
- [IsDisjoint](#IsDisjoint)
- [HasKey](#HasKey)
- [MapToStruct](#MapToStruct)
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
- [NewConcurrentMap](#NewConcurrentMap)
- [ConcurrentMap_Get](#ConcurrentMap_Get)
- [ConcurrentMap_Set](#ConcurrentMap_Set)
@@ -52,6 +55,7 @@ import (
- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete)
- [ConcurrentMap_Has](#ConcurrentMap_Has)
- [ConcurrentMap_Range](#ConcurrentMap_Range)
- [GetOrSet](#GetOrSet)
<div STYLE="page-break-after: always;"></div>
@@ -992,6 +996,144 @@ func main() {
}
```
### <span id="MapToStruct">MapToStruct</span>
<p>Converts map to struct</p>
<b>Signature:</b>
```go
func MapToStruct(m map[string]any, structObj any) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/7wYyVfX38Dp)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
personReqMap := map[string]any{
"name": "Nothin",
"max_age": 35,
"page": 1,
"pageSize": 10,
}
type PersonReq struct {
Name string `json:"name"`
MaxAge int `json:"max_age"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
var personReq PersonReq
_ = maputil.MapToStruct(personReqMap, &personReq)
fmt.Println(personReq)
// Output:
// {Nothin 35 1 10}
}
```
### <span id="ToSortedSlicesDefault">ToSortedSlicesDefault</span>
<p>
Translate the key and value of the map into two slices that are sorted in ascending order according to the 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](https://go.dev/play/p/43gEM2po-qy)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys, values := maputil.ToSortedSlicesDefault(m)
fmt.Println(keys)
fmt.Println(values)
// Output:
// [1 2 3]
// [a b c]
}
```
### <span id="ToSortedSlicesWithComparator">ToSortedSlicesWithComparator</span>
<p>
Translate the key and value of the map into two slices that are sorted according to a custom sorting rule defined by a comparator function based on the key's value, with the position of the elements in the value slice corresponding to the key.
</p>
<b>Signature:</b>
```go
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/0nlPo6YLdt3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m1 := map[time.Time]string{
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
}
keys1, values1 := maputil.ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
return a.Before(b)
})
m2 := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys2, values2 := maputil.ToSortedSlicesWithComparator(m2, func(a, b int) bool {
return a > b
})
fmt.Println(keys2)
fmt.Println(values2)
fmt.Println(keys1)
fmt.Println(values1)
// Output:
// [3 2 1]
// [c b a]
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
// [yesterday today tomorrow]
}
```
### <span id="NewConcurrentMap">NewConcurrentMap</span>
<p>ConcurrentMap is like map, but is safe for concurrent use by multiple goroutines.</p>
@@ -1055,15 +1197,15 @@ func main() {
var wg2 sync.WaitGroup
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
val, ok := cm.Get(fmt.Sprintf("%d", n))
fmt.Println(val, ok)
wg2.Done()
}(j)
}
wg2.Wait()
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
val, ok := cm.Get(fmt.Sprintf("%d", n))
fmt.Println(val, ok)
wg2.Done()
}(j)
}
wg2.Wait()
// output: (order may change)
// 1 true
@@ -1110,15 +1252,15 @@ func main() {
var wg2 sync.WaitGroup
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
val, ok := cm.Get(fmt.Sprintf("%d", n))
fmt.Println(val, ok)
wg2.Done()
}(j)
}
wg2.Wait()
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
val, ok := cm.Get(fmt.Sprintf("%d", n))
fmt.Println(val, ok)
wg2.Done()
}(j)
}
wg2.Wait()
// output: (order may change)
// 1 true
@@ -1208,7 +1350,7 @@ func main() {
wg1.Wait()
var wg2 sync.WaitGroup
wg2.Add(5)
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
cm.Delete(fmt.Sprintf("%d", n))
@@ -1255,7 +1397,7 @@ func main() {
wg1.Wait()
var wg2 sync.WaitGroup
wg2.Add(5)
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n))
@@ -1307,7 +1449,7 @@ func main() {
wg1.Wait()
var wg2 sync.WaitGroup
wg2.Add(5)
wg2.Add(5)
for j := 0; j < 5; j++ {
go func(n int) {
ok := cm.Has(fmt.Sprintf("%d", n))
@@ -1360,3 +1502,40 @@ func main() {
})
}
```
### <span id="GetOrSet">GetOrSet</span>
<p>Returns value of the given key or set the given value value if not present.</p>
<b>Signature:</b>
```go
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/IVQwO1OkEJC)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
1: "a",
}
result1 := maputil.GetOrSet(m, 1, "1")
result2 := maputil.GetOrSet(m, 2, "b")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// a
// b
}
```

View File

@@ -508,7 +508,7 @@ func main() {
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/8hOeSADZPCo)</span></b>
```go
package main
@@ -544,7 +544,7 @@ func main() {
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wy5bYEyUKKG)</span></b>
```go
package main
@@ -577,10 +577,10 @@ func main() {
<b>Signature:</b>
```go
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/vbCBrQHZEED)</span></b>
```go
package main
@@ -613,10 +613,10 @@ func main() {
<b>Signature:</b>
```go
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Qk9KPd2IdDb)</span></b>
```go
package main
@@ -1130,7 +1130,7 @@ func main() {
### <span id="Div">Div</span>
<p>returns the result of x divided by y.</p>
<p>Returns the result of x divided by y.</p>
<b>Signature:</b>
@@ -1138,7 +1138,7 @@ func main() {
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/WLxDdGXXYat)</span></b>
```go
package main

View File

@@ -624,7 +624,9 @@ func main() {
### <span id="HttpGet">HttpGet</span>
<p>Send http get request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http get request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -666,7 +668,9 @@ func main() {
### <span id="HttpPost">HttpPost</span>
<p>Send http post request.(Deprecated: use SendRequest for replacement)</p>
<p>Send http post request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -713,7 +717,9 @@ func main() {
### <span id="HttpPut">HttpPut</span>
<p>Send http put request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http put request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -763,7 +769,9 @@ func main() {
### <span id="HttpDelete">HttpDelete</span>
<p>Send http delete request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http delete request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -802,7 +810,9 @@ func main() {
### <span id="HttpPatch">HttpPatch</span>
<p>Send http patch request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http patch request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>

View File

@@ -24,8 +24,8 @@ import (
- [Of](#Of)
- [Unwrap](#Unwrap)
- [UnwarpOr](#UnwarpOr)
- [UnwarpOrDefault](#UnwarpOrDefault)
- [UnwrapOr](#UnwrapOr)
- [UnwrapOrDefault](#UnwrapOrDefault)
- [ExtractPointer](#ExtractPointer)
<div STYLE="page-break-after: always;"></div>
@@ -103,13 +103,13 @@ func main() {
```
### <span id="UnwarpOr">UnwarpOr</span>
### <span id="UnwrapOr">UnwrapOr</span>
<p>Returns the value from the pointer or fallback if the pointer is nil.</p>
<b>Signature:</b>
```go
UnwarpOr[T any](p *T, fallback T) T
UnwrapOr[T any](p *T, fallback T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/mmNaLC38W8C)</span></b>
@@ -129,10 +129,10 @@ func main() {
var c *int
var d *string
result1 := pointer.UnwarpOr(&a, 456)
result2 := pointer.UnwarpOr(&b, "abc")
result3 := pointer.UnwarpOr(c, 456)
result4 := pointer.UnwarpOr(d, "def")
result1 := pointer.UnwrapOr(&a, 456)
result2 := pointer.UnwrapOr(&b, "abc")
result3 := pointer.UnwrapOr(c, 456)
result4 := pointer.UnwrapOr(d, "def")
fmt.Println(result1)
fmt.Println(result2)
@@ -148,13 +148,13 @@ func main() {
```
### <span id="UnwarpOrDefault">UnwarpOrDefault</span>
### <span id="UnwrapOrDefault">UnwrapOrDefault</span>
<p>Returns the value from the pointer or the default value if the pointer is nil.</p>
<b>Signature:</b>
```go
UnwarpOrDefault[T any](p *T) T
UnwrapOrDefault[T any](p *T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
@@ -174,10 +174,10 @@ func main() {
var c *int
var d *string
result1 := pointer.UnwarpOrDefault(&a)
result2 := pointer.UnwarpOrDefault(&b)
result3 := pointer.UnwarpOrDefault(c)
result4 := pointer.UnwarpOrDefault(d)
result1 := pointer.UnwrapOrDefault(&a)
result2 := pointer.UnwrapOrDefault(&b)
result3 := pointer.UnwrapOrDefault(c)
result4 := pointer.UnwrapOrDefault(d)
fmt.Println(result1)
fmt.Println(result2)

View File

@@ -331,7 +331,7 @@ func main() {
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jIm_o2vb5Y4)</span></b>
```go
package main
@@ -361,7 +361,7 @@ func main() {
return errors.New("error occurs")
}
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
err := retry.Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
if err != nil {
return
}
@@ -384,7 +384,7 @@ func main() {
func RetryWithLinearBackoff(interval time.Duration) Option
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/nk2XRmagfVF)</span></b>
```go
package main
@@ -429,7 +429,7 @@ func main() {
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/xp1avQmn16X)</span></b>
```go
package main

View File

@@ -86,6 +86,7 @@ import (
- [ToSlicePointer](#ToSlicePointer)
- [Unique](#Unique)
- [UniqueBy](#UniqueBy)
- [UniqueByField](#UniqueByField)
- [Union](#Union)
- [UnionBy](#UnionBy)
- [UpdateAt](#UpdateAt)
@@ -93,6 +94,10 @@ import (
- [KeyBy](#KeyBy)
- [Join](#Join)
- [Partition](#Partition)
- [SetToDefaultIf](#SetToDefaultIf)
- [Break](#Break)
- [RightPadding](#RightPadding)
- [LeftPadding](#LeftPadding)
<div STYLE="page-break-after: always;"></div>
@@ -317,12 +322,12 @@ func main() {
### <span id="Concat">Concat</span>
<p>Creates a new slice concatenating slice with any additional slices.</p>
<p>Concat creates a new slice concatenating slice with any additional slices.</p>
<b>Signature:</b>
```go
func Concat[T any](slice []T, slices ...[]T) []T
func Concat[T any](slices ...[]T) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
@@ -890,10 +895,12 @@ func main() {
}
```
### <span id="Find">Find(deprecated: use FindBy)</span>
### <span id="Find">Find</span>
<p>Iterates over elements of slice, returning the first one that passes a truth test on function.</p>
> ⚠️ This function is deprecated. use `FindBy` instead.
<b>Signature:</b>
```go
@@ -962,10 +969,12 @@ func main() {
}
```
### <span id="FindLast">FindLast(deprecated: use FindLastBy)</span>
### <span id="FindLast">FindLast</span>
<p>iterates over elements of slice from end to begin, returning the last one that passes a truth test on function.</p>
> ⚠️ This function is deprecated. use `FindLastBy` instead.
<b>Signature:</b>
```go
@@ -1237,10 +1246,12 @@ func main() {
}
```
### <span id="IntSlice">IntSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
### <span id="IntSlice">IntSlice</span>
<p>Convert interface slice to int slice.</p>
> ⚠️ This function is deprecated. Use generic feature of go1.18+ for replacement.
<b>Signature:</b>
```go
@@ -1266,10 +1277,12 @@ func main() {
}
```
### <span id="InterfaceSlice">InterfaceSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
### <span id="InterfaceSlice">InterfaceSlice</span>
<p>Convert value to interface slice.</p>
> ⚠️ This function is deprecated. Use generic feature of go1.18+ for replacement.
<b>Signature:</b>
```go
@@ -1540,6 +1553,8 @@ func main() {
<p>Merge all given slices into one slice.</p>
> ⚠️ This function is deprecated. use `Concat` instead.
<b>Signature:</b>
```go
@@ -1599,7 +1614,9 @@ func main() {
### <span id="Reduce">Reduce</span>
<p>Reduce slice.(Deprecated: use ReduceBy)</p>
<p>Reduce slice.</p>
> ⚠️ This function is deprecated. use `ReduceBy` instead.
<b>Signature:</b>
@@ -2125,10 +2142,12 @@ func main() {
}
```
### <span id="StringSlice">StringSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
### <span id="StringSlice">StringSlice</span>
<p>Convert interface slice to string slice.</p>
> ⚠️ This function is deprecated. use generic feature of go1.18+ for replacement
<b>Signature:</b>
```go
@@ -2277,12 +2296,12 @@ func main() {
### <span id="UniqueBy">UniqueBy</span>
<p>Call iteratee func with every item of slice, then remove duplicated.</p>
<p>Removes duplicate elements from the input slice based on the values returned by the iteratee function. this function maintains the order of the elements.</p>
<b>Signature:</b>
```go
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/UR323iZLDpv)</span></b>
@@ -2302,7 +2321,48 @@ func main() {
fmt.Println(result)
// Output:
// [1 2 0]
// [1 2 3]
}
```
### <span id="UniqueByField">UniqueByField</span>
<p>Remove duplicate elements in struct slice by struct field.</p>
<b>Signature:</b>
```go
func UniqueByField[T any](slice []T, field string) ([]T, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Runs](https://go.dev/play/p/6cifcZSPIGu)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
users := []User{
{ID: 1, Name: "a"},
{ID: 2, Name: "b"},
{ID: 1, Name: "c"},
}
result, err := slice.UniqueByField(users, "ID")
if err != nil {
}
fmt.Println(result)
// Output:
// [{1 a} {2 b}]
}
```
@@ -2577,7 +2637,7 @@ func main() {
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/9AXGlPRC0-A)</span></b>
```go
import (
@@ -2596,4 +2656,91 @@ func main() {
// [ 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:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/yLYcBTyeQIz)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
even := func(n int) bool { return n%2 == 0 }
resultEven, resultAfterFirstEven := slice.Break(nums, even)
fmt.Println(resultEven)
fmt.Println(resultAfterFirstEven)
// Output:
// [1]
// [2 3 4 5]
}
```
<span id="c">RightPadding</span>
<p>RightPadding adds padding to the right end of a slice.</p>
<b>Signature:</b>
```go
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/0_2rlLEMBXL)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
}
```
<span id="LeftPadding">LeftPadding</span>
<p>LeftPadding adds padding to the left begin of a slice.</p>
<b>Signature:</b>
```go
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jlQVoelLl2k)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
```

View File

@@ -62,6 +62,7 @@ import (
- [RemoveWhiteSpace](#RemoveWhiteSpace)
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
<div STYLE="page-break-after: always;"></div>
@@ -1477,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 (
@@ -1507,10 +1508,10 @@ func main() {
<b>Signature:</b>
```go
HammingDistance(a, b string) (int, error)
func HammingDistance(a, b string) (int, error)
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/glNdQEA9HUi)</span></b>
```go
import (
@@ -1530,4 +1531,39 @@ func main() {
// 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

@@ -33,7 +33,7 @@ features:
details: Well structure, test for every exported function.
---
<p style="position:relative; top: -316px;left: 560px;">
<p style="position:relative; inline-block;top: -330px;left: 30%">
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
</p>

View File

@@ -33,7 +33,7 @@ features:
details: 结构良好,测试每个导出的函数。
---
<p style="position:relative;display: inline-block;top: -316px;left: 32%">
<p style="position:relative;display: inline-block;top: -330px;left: 30%">
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
</p>

1171
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,6 @@
"docs:preview": "vitepress preview"
},
"devDependencies": {
"vitepress": "^1.0.0-rc.4"
"vitepress": "^1.2.3"
}
}

View File

@@ -119,58 +119,43 @@ func CreateDir(absPath string) error {
// if dstPath exists, it will return an error.
// Play: https://go.dev/play/p/YAyFTA_UuPb
func CopyDir(srcPath string, dstPath string) error {
if !IsDir(srcPath) {
return errors.New("source path is not a directory")
}
var err error
srcPath, err = filepath.Abs(srcPath)
srcInfo, err := os.Stat(srcPath)
if err != nil {
return err
}
if IsExist(dstPath) {
return errors.New("destination path already exists")
}
dstPath, err = filepath.Abs(dstPath)
if err != nil {
return err
return fmt.Errorf("failed to get source directory info: %w", err)
}
// get srcPath's file info
srcFileInfo, err := os.Stat(srcPath)
if err != nil {
return err
if !srcInfo.IsDir() {
return fmt.Errorf("source path is not a directory: %s", srcPath)
}
// create dstPath with srcPath's mode
err = os.MkdirAll(dstPath, srcFileInfo.Mode())
err = os.MkdirAll(dstPath, 0755)
if err != nil {
return err
return fmt.Errorf("failed to create destination directory: %w", err)
}
err = filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error {
if srcPath == path {
return nil
}
curDstPath := filepath.Join(dstPath, filepath.Base(path))
if info.IsDir() {
err = CopyDir(path, curDstPath)
entries, err := os.ReadDir(srcPath)
if err != nil {
return fmt.Errorf("failed to read source directory: %w", err)
}
for _, entry := range entries {
srcDir := filepath.Join(srcPath, entry.Name())
dstDir := filepath.Join(dstPath, entry.Name())
if entry.IsDir() {
err := CopyDir(srcDir, dstDir)
if err != nil {
return err
}
} else {
err = CopyFile(path, curDstPath)
if err != nil {
return err
}
err = os.Chmod(curDstPath, info.Mode())
err := CopyFile(srcDir, dstDir)
if err != nil {
return err
}
}
return err
})
}
return err
return nil
}
// IsDir checks if the path is directory or not.
@@ -869,7 +854,7 @@ func isCsvSupportedType(v interface{}) bool {
}
// ChunkRead reads a block from the file at the specified offset and returns all lines within the block
// Play: todo
// 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) // 从指定偏移读取数据到缓冲区
@@ -901,7 +886,7 @@ func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]str
// chunkSizeMB 分块的大小单位MB设置为0时使用默认100MB,设置过大反而不利,视情调整
// maxGoroutine 并发读取分块的数量设置为0时使用CPU核心数
// linesCh用于接收返回结果的通道。
// Play: todo
// Play: https://go.dev/play/p/teMXnCsdSEw
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error {
if chunkSizeMB == 0 {
chunkSizeMB = 100

View File

@@ -84,13 +84,26 @@ var (
// The precision parameter specifies the number of digits after the decimal point, which defaults to 4.
// Play: https://go.dev/play/p/FPXs1suwRcs
func DecimalBytes(size float64, precision ...int) string {
p := 5
pointPosition := 4
if len(precision) > 0 {
p = precision[0] + 1
pointPosition = precision[0]
}
size, unit := calculateByteSize(size, 1000.0, decimalByteUnits)
return fmt.Sprintf("%.*g%s", p, size, unit)
format := fmt.Sprintf("%%.%df", pointPosition)
bytes := fmt.Sprintf(format, size)
for i := len(bytes); i > 0; i-- {
s := bytes[i-1]
if s != '0' && s != '.' {
break
}
bytes = bytes[:i-1]
}
return bytes + unit
}
// BinaryBytes returns a human-readable byte size under binary standard (base 1024)

View File

@@ -20,6 +20,11 @@ func TestDecimalBytes(t *testing.T) {
assert.Equal("3.123PB", DecimalBytes(float64(3.123*UnitPB)))
assert.Equal("4.123EB", DecimalBytes(float64(4.123*UnitEB)))
assert.Equal("1EB", DecimalBytes(float64(1000*UnitPB)))
assert.Equal("62MB", DecimalBytes(61812496, 0))
assert.Equal("61.8MB", DecimalBytes(61812496, 1))
assert.Equal("401MB", DecimalBytes(401000000))
assert.Equal("401MB", DecimalBytes(401000000, 0))
assert.Equal("4MB", DecimalBytes(4010000, 0))
}
func TestBinaryBytes(t *testing.T) {

View File

@@ -7,6 +7,7 @@ package function
import (
"fmt"
"reflect"
"sync"
"time"
)
@@ -84,22 +85,44 @@ func Delay(delay time.Duration, fn any, args ...any) {
}
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
// Deprecated: Use Debounce function instead.
// Play: https://go.dev/play/p/absuEGB_GN7
func Debounced(fn func(), duration time.Duration) func() {
// Catch programming error while constructing the closure
mustBeFunction(fn)
func Debounced(fn func(), delay time.Duration) func() {
debouncedFn, _ := Debounce(fn, delay)
return debouncedFn
}
timer := time.NewTimer(duration)
timer.Stop()
// Debounce creates a debounced version of the provided function.
// Play: todo
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func()) {
var (
timer *time.Timer
mu sync.Mutex
)
go func() {
for {
<-timer.C
go fn()
debouncedFn = func() {
mu.Lock()
defer mu.Unlock()
if timer != nil {
timer.Stop()
}
}()
return func() { timer.Reset(duration) }
timer = time.AfterFunc(delay, func() {
fn()
})
}
cancelFn = func() {
mu.Lock()
defer mu.Unlock()
if timer != nil {
timer.Stop()
}
}
return debouncedFn, cancelFn
}
// Schedule invoke function every duration time, util close the returned bool channel.
@@ -139,6 +162,7 @@ func Pipeline[T any](funcs ...func(T) T) func(T) T {
// AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
// 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")

View File

@@ -79,6 +79,32 @@ func ExampleDelay() {
// hello
}
func ExampleDebounce() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
func ExampleDebounced() {
count := 0
add := func() {

View File

@@ -125,6 +125,80 @@ func TestDebounced(t *testing.T) {
assert.Equal(2, count)
}
func TestDebounce(t *testing.T) {
assert := internal.NewAssert(t, "TestDebounce")
t.Run("Single call", func(t *testing.T) {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := Debounce(fn, 500*time.Millisecond)
debouncedFn()
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Multiple calls within debounce interval", func(t *testing.T) {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := Debounce(fn, 1*time.Second)
for i := 0; i < 5; i++ {
go func(index int) {
time.Sleep(time.Duration(index) * 200 * time.Millisecond)
debouncedFn()
}(i)
}
time.Sleep(2 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Immediate consecutive calls", func(t *testing.T) {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Cancel calls", func(t *testing.T) {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, cancelFn := Debounce(fn, 500*time.Millisecond)
debouncedFn()
cancelFn()
time.Sleep(1 * time.Second)
assert.Equal(0, callCount)
})
}
func TestSchedule(t *testing.T) {
// assert := internal.NewAssert(t, "TestSchedule")

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

@@ -7,6 +7,10 @@ package maputil
import (
"fmt"
"reflect"
"sort"
"strings"
"golang.org/x/exp/constraints"
"github.com/duke-git/lancet/v2/slice"
)
@@ -66,7 +70,12 @@ func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T
// Merge maps, next key will overwrite previous key.
// Play: https://go.dev/play/p/H95LENF1uB-
func Merge[K comparable, V any](maps ...map[K]V) map[K]V {
result := make(map[K]V, 0)
size := 0
for i := range maps {
size += len(maps[i])
}
result := make(map[K]V, size)
for _, m := range maps {
for k, v := range m {
@@ -306,7 +315,7 @@ func HasKey[K comparable, V any](m map[K]V, key K) bool {
}
// MapToStruct converts map to struct
// Play: todo
// Play: https://go.dev/play/p/7wYyVfX38Dp
func MapToStruct(m map[string]any, structObj any) error {
for k, v := range m {
err := setStructField(structObj, k, v)
@@ -375,8 +384,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 +392,64 @@ 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.
// Play: https://go.dev/play/p/43gEM2po-qy
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V) {
keys := make([]K, 0, len(m))
// store the 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.
// Play: https://go.dev/play/p/0nlPo6YLdt3
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V) {
keys := make([]K, 0, len(m))
// store the 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
}
// GetOrSet returns value of the given key or set the given value value if not present.
// Play: https://go.dev/play/p/IVQwO1OkEJC
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V {
if v, ok := m[key]; ok {
return v
}
m[key] = value
return value
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"sort"
"strconv"
"time"
)
func ExampleKeys() {
@@ -450,3 +451,92 @@ 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]
}
func ExampleGetOrSet() {
m := map[int]string{
1: "a",
}
result1 := GetOrSet(m, 1, "1")
result2 := GetOrSet(m, 2, "b")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// a
// b
}

View File

@@ -1,9 +1,11 @@
package maputil
import (
"math/cmplx"
"sort"
"strconv"
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
@@ -105,14 +107,14 @@ func TestMerge(t *testing.T) {
2: "b",
}
m2 := map[int]string{
1: "1",
3: "2",
2: "c",
3: "d",
}
expected := map[int]string{
1: "1",
2: "b",
3: "2",
1: "a",
2: "c",
3: "d",
}
acturl := Merge(m1, m2)
@@ -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,197 @@ 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)
})
}
}
func TestGetOrSet(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGetOrSet")
m := map[int]string{
1: "a",
}
result1 := GetOrSet(m, 1, "1")
result2 := GetOrSet(m, 2, "b")
assert.Equal("a", result1)
assert.Equal("b", result2)
}

View File

@@ -101,7 +101,7 @@ func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T {
}
// FloorToFloat round down to n decimal places.
// Play: todo
// Play: https://go.dev/play/p/vbCBrQHZEED
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
tmp := math.Pow(10.0, float64(n))
x *= T(tmp)
@@ -110,7 +110,7 @@ func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
}
// FloorToString round down to n decimal places.
// Play: todo
// 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)
@@ -120,7 +120,7 @@ func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
}
// CeilToFloat round up to n decimal places.
// Play: todo
// Play: https://go.dev/play/p/8hOeSADZPCo
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
tmp := math.Pow(10.0, float64(n))
x *= T(tmp)
@@ -129,7 +129,7 @@ func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
}
// CeilToString round up to n decimal places.
// Play: todo
// 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)
@@ -391,7 +391,7 @@ func Abs[T constraints.Integer | constraints.Float](x T) T {
}
// Div returns the result of x divided by y.
// Play: todo
// Play: https://go.dev/play/p/WLxDdGXXYat
func Div[T constraints.Float | constraints.Integer](x T, y T) float64 {
return float64(x) / float64(y)
}

View File

@@ -28,7 +28,6 @@ import (
"strings"
"time"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/slice"
)
@@ -103,19 +102,22 @@ type HttpRequest struct {
// HttpClientConfig contains some configurations for http client
type HttpClientConfig struct {
Timeout time.Duration
SSLEnabled bool
TLSConfig *tls.Config
Compressed bool
HandshakeTimeout time.Duration
ResponseTimeout time.Duration
Verbose bool
Proxy *url.URL
}
// defaultHttpClientConfig defalut client config.
var defaultHttpClientConfig = &HttpClientConfig{
Timeout: 50 * time.Second,
Compressed: false,
HandshakeTimeout: 20 * time.Second,
ResponseTimeout: 40 * time.Second,
HandshakeTimeout: 10 * time.Second,
ResponseTimeout: 10 * time.Second,
}
// HttpClient is used for sending http request.
@@ -131,6 +133,7 @@ type HttpClient struct {
func NewHttpClient() *HttpClient {
client := &HttpClient{
Client: &http.Client{
Timeout: defaultHttpClientConfig.Timeout,
Transport: &http.Transport{
TLSHandshakeTimeout: defaultHttpClientConfig.HandshakeTimeout,
ResponseHeaderTimeout: defaultHttpClientConfig.ResponseTimeout,
@@ -164,6 +167,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 +371,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

@@ -11,17 +11,24 @@ import (
// Of returns a pointer to the value `v`.
// Play: https://go.dev/play/p/HFd70x4DrMj
func Of[T any](v T) *T {
if IsNil(v) {
return nil
}
return &v
}
// Unwrap returns the value from the pointer.
// Play: https://go.dev/play/p/cgeu3g7cjWb
//
// Play: https://go.dev/play/p/cgeu3g7cjWb
// Deprecated: Please use UnwrapOr
func Unwrap[T any](p *T) T {
return *p
}
// UnwarpOr returns the value from the pointer or fallback if the pointer is nil.
// Play: https://go.dev/play/p/mmNaLC38W8C
//
// Play: https://go.dev/play/p/mmNaLC38W8C
// Deprecated: Please use UnwrapOr
func UnwarpOr[T any](p *T, fallback T) T {
if p == nil {
return fallback
@@ -30,7 +37,9 @@ func UnwarpOr[T any](p *T, fallback T) T {
}
// UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
// Play: https://go.dev/play/p/ZnGIHf8_o4E
//
// Play: https://go.dev/play/p/ZnGIHf8_o4E
// Deprecated: Please use UnwrapOr
func UnwarpOrDefault[T any](p *T) T {
var v T
@@ -40,9 +49,24 @@ func UnwarpOrDefault[T any](p *T) T {
return *p
}
// UnwrapOr returns the value from the pointer or fallback if the pointer is nil.
func UnwrapOr[T any](p *T, fallback ...T) T {
if !IsNil(p) {
return *p
}
if len(fallback) > 0 {
return fallback[0]
}
var t T
return t
}
// ExtractPointer returns the underlying value by the given interface type
// Play: https://go.dev/play/p/D7HFjeWU2ZP
func ExtractPointer(value any) any {
if IsNil(value) {
return value
}
t := reflect.TypeOf(value)
v := reflect.ValueOf(value)
@@ -56,3 +80,8 @@ func ExtractPointer(value any) any {
return nil
}
// IsNil returns true if the given interface is nil or the underlying value is nil.
func IsNil(i interface{}) bool {
return i == nil || (reflect.ValueOf(i).Kind() == reflect.Ptr && reflect.ValueOf(i).IsNil())
}

View File

@@ -90,3 +90,61 @@ func ExampleExtractPointer() {
// Output:
// 1
}
func ExampleIsNil() {
a := 1
b := &a
c := &b
d := &c
e := &d
var f *int
result1 := IsNil(a)
result2 := IsNil(b)
result3 := IsNil(c)
result4 := IsNil(d)
result5 := IsNil(e)
result6 := IsNil(f)
result7 := IsNil(nil)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
fmt.Println(result7)
// Output:
// false
// false
// false
// false
// false
// true
// true
}
func ExampleUnwrapOr() {
a := 123
b := "abc"
var c *int
var d *string
result1 := UnwrapOr(&a, 456)
result2 := UnwrapOr(&b, "efg")
result3 := UnwrapOr(c, 456)
result4 := UnwrapOr(d, "def")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// 123
// abc
// 456
// def
}

View File

@@ -47,10 +47,10 @@ func TestUnwarpOr(t *testing.T) {
assert.Equal("def", UnwarpOr(d, "def"))
}
func TestUnwarpOrDefault(t *testing.T) {
func TestUnwrapOrDefault(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUnwarpOrDefault")
assert := internal.NewAssert(t, "TestUnwrapOrDefault")
a := 123
b := "abc"

View File

@@ -45,7 +45,7 @@ func RetryTimes(n uint) Option {
}
// RetryWithCustomBackoff set abitary custom backoff strategy
// Play: todo
// Play: https://go.dev/play/p/jIm_o2vb5Y4
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
// Play: todo
// Play: https://go.dev/play/p/PDet2ZQZwcB
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
// Play: todo
// Play: https://go.dev/play/p/xp1avQmn16X
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,26 +92,32 @@ 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...)
func Concat[T any](slices ...[]T) []T {
totalLen := 0
for _, v := range slices {
totalLen += len(v)
if totalLen < 0 {
panic("len out of range")
}
}
result := make([]T, 0, totalLen)
for _, v := range slices {
result = append(result, v...)
@@ -109,7 +126,7 @@ func Concat[T any](slice []T, slices ...[]T) []T {
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{}
@@ -754,36 +771,105 @@ func UpdateAt[T any](slice []T, index int, value T) []T {
// Unique remove duplicate elements in slice.
// Play: https://go.dev/play/p/AXw0R3ZTE6a
func Unique[T comparable](slice []T) []T {
result := []T{}
result := make([]T, 0, len(slice))
seen := make(map[T]struct{}, len(slice))
for i := 0; i < len(slice); i++ {
v := slice[i]
skip := true
for j := range result {
if v == result[j] {
skip = false
for i := range slice {
if _, ok := seen[slice[i]]; ok {
continue
}
seen[slice[i]] = struct{}{}
result = append(result, slice[i])
}
return result
}
// UniqueBy removes duplicate elements from the input slice based on the values returned by the iteratee function.
// The function maintains the order of the elements.
// Play: todo
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T {
result := make([]T, 0, len(slice))
seen := make(map[U]struct{}, len(slice))
for i := range slice {
key := iteratee(slice[i])
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
result = append(result, slice[i])
}
return result
}
// UniqueByComparator removes duplicate elements from the input slice using the provided comparator function.
// The function maintains the order of the elements.
// Play: todo
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T {
result := make([]T, 0, len(slice))
seen := make([]T, 0, len(slice))
for _, item := range slice {
duplicate := false
for _, seenItem := range seen {
if comparator(item, seenItem) {
duplicate = true
break
}
}
if skip {
result = append(result, v)
if !duplicate {
seen = append(seen, item)
result = append(result, item)
}
}
return result
}
// UniqueBy call iteratee func with every item of slice, then remove duplicated.
// Play: https://go.dev/play/p/UR323iZLDpv
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T {
result := []T{}
// UniqueByField remove duplicate elements in struct slice by struct field.
// Play: https://go.dev/play/p/6cifcZSPIGu
func UniqueByField[T any](slice []T, field string) ([]T, error) {
seen := map[any]struct{}{}
for _, v := range slice {
val := iteratee(v)
result = append(result, val)
var result []T
for _, item := range slice {
val, err := getField(item, field)
if err != nil {
return nil, fmt.Errorf("get field %s failed: %v", field, err)
}
if _, ok := seen[val]; !ok {
seen[val] = struct{}{}
result = append(result, item)
}
}
return Unique(result)
return result, nil
}
func getField[T any](item T, field string) (interface{}, error) {
v := reflect.ValueOf(item)
t := reflect.TypeOf(item)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", item)
}
f := v.FieldByName(field)
if !f.IsValid() {
return nil, fmt.Errorf("field name %s not found", field)
}
return v.FieldByName(field).Interface(), nil
}
// Union creates a slice of unique elements, in order, from all given slices.
@@ -823,16 +909,11 @@ func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T {
return result
}
// Deprecated: Please use Concat() function instead.
// 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)
for _, v := range slices {
result = append(result, v...)
}
return result
return Concat(slices...)
}
// Intersection creates a slice of unique elements that included by all slices.
@@ -1173,7 +1254,7 @@ func AppendIfAbsent[T comparable](slice []T, item T) []T {
// SetToDefaultIf sets elements to their default value if they match the given predicate.
// It retains the positions of the elements in the slice.
// It returns slice of T and the count of modified slice items
// Play: todo
// Play: https://go.dev/play/p/9AXGlPRC0-A
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int) {
var count int
for i := 0; i < len(slice); i++ {
@@ -1239,6 +1320,30 @@ func Partition[T any](slice []T, predicates ...func(item T) bool) [][]T {
return result
}
// Breaks a list into two parts at the point where the predicate for the first time is true.
// Play: https://go.dev/play/p/yLYcBTyeQIz
func Break[T any](values []T, predicate func(T) bool) ([]T, []T) {
a := make([]T, 0)
b := make([]T, 0)
if len(values) == 0 {
return a, b
}
matched := false
for _, value := range values {
if !matched && predicate(value) {
matched = true
}
if matched {
b = append(b, value)
} else {
a = append(a, value)
}
}
return a, b
}
// Random get a random item of slice, return idx=-1 when slice is empty
// Play: https://go.dev/play/p/UzpGQptWppw
func Random[T any](slice []T) (val T, idx int) {
@@ -1249,3 +1354,35 @@ func Random[T any](slice []T) (val T, idx int) {
idx = random.RandInt(0, len(slice))
return slice[idx], idx
}
// RightPadding adds padding to the right end of a slice.
// Play: https://go.dev/play/p/0_2rlLEMBXL
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
if paddingLength == 0 {
return slice
}
for i := 0; i < paddingLength; i++ {
slice = append(slice, paddingValue)
}
return slice
}
// LeftPadding adds padding to the left begin of a slice.
// Play: https://go.dev/play/p/jlQVoelLl2k
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
if paddingLength == 0 {
return slice
}
paddedSlice := make([]T, len(slice)+paddingLength)
i := 0
for ; i < paddingLength; i++ {
paddedSlice[i] = paddingValue
}
for j := 0; j < len(slice); j++ {
paddedSlice[i] = slice[j]
i++
}
return paddedSlice
}

View File

@@ -5,6 +5,7 @@ import (
"math"
"reflect"
"strconv"
"strings"
)
func ExampleContain() {
@@ -777,7 +778,46 @@ func ExampleUniqueBy() {
fmt.Println(result)
// Output:
// [1 2 0]
// [1 2 3]
}
func ExampleUniqueByComparator() {
uniqueNums := UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
return item == other
})
caseInsensitiveStrings := UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool {
return strings.ToLower(item) == strings.ToLower(other)
})
fmt.Println(uniqueNums)
fmt.Println(caseInsensitiveStrings)
// Output:
// [1 2 3 4 5 6]
// [apple banana cherry date]
}
func ExampleUniqueByField() {
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
users := []User{
{ID: 1, Name: "a"},
{ID: 2, Name: "b"},
{ID: 1, Name: "c"},
}
result, err := UniqueByField(users, "ID")
if err != nil {
}
fmt.Println(result)
// Output:
// [{1 a} {2 b}]
}
func ExampleUnion() {
@@ -1112,3 +1152,44 @@ func ExampleSetToDefaultIf() {
// [ 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]
}
func ExampleUniqueByParallel() {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
numOfThreads := 4
comparator := func(item int, other int) bool { return item == other }
result := UniqueByParallel(nums, numOfThreads, comparator)
fmt.Println(result)
// Output:
// [1 2 3 4 5 6 7]
}

96
slice/slice_parallel.go Normal file
View File

@@ -0,0 +1,96 @@
// Copyright 2024 dudaodong@gmail.com. All rights resulterved.
// Use of this source code is governed by MIT license
package slice
import (
"runtime"
"sync"
)
// UniqueByParallel removes duplicate elements from the slice by parallel
// The comparator function is used to compare the elements
// The numOfThreads parameter specifies the number of threads to use
// If numOfThreads is less than or equal to 0, it will be set to 1
// The comparator function should return true if the two elements are equal
// Play: todo
func UniqueByParallel[T comparable](slice []T, numOfThreads int, comparator func(item T, other T) bool) []T {
if numOfThreads <= 0 {
numOfThreads = 1
} else if numOfThreads > len(slice) {
numOfThreads = len(slice)
}
maxThreads := runtime.NumCPU()
if numOfThreads > maxThreads {
numOfThreads = maxThreads
}
removeDuplicate := func(items []T, comparator func(item T, other T) bool) []T {
var result []T
for _, item := range items {
seen := false
for _, r := range result {
if comparator(item, r) {
seen = true
break
}
}
if !seen {
result = append(result, item)
}
}
return result
}
chunkSize := (len(slice) + numOfThreads - 1) / numOfThreads
chunks := make([][]T, 0, numOfThreads)
for i := 0; i < len(slice); i += chunkSize {
end := i + chunkSize
if end > len(slice) {
end = len(slice)
}
chunks = append(chunks, slice[i:end])
}
type resultChunk struct {
index int
data []T
}
resultCh := make(chan resultChunk, numOfThreads)
var wg sync.WaitGroup
for i, chunk := range chunks {
wg.Add(1)
go func(index int, chunk []T) {
defer wg.Done()
resultCh <- resultChunk{index, removeDuplicate(chunk, comparator)}
}(i, chunk)
}
go func() {
wg.Wait()
close(resultCh)
}()
results := make([][]T, len(chunks))
for r := range resultCh {
results[r.index] = r.data
}
result := []T{}
seen := make(map[T]bool)
for _, chunk := range results {
for _, item := range chunk {
if !seen[item] {
seen[item] = true
result = append(result, item)
}
}
}
return result
}

View File

@@ -5,6 +5,7 @@ import (
"math"
"reflect"
"strconv"
"strings"
"testing"
"github.com/duke-git/lancet/v2/internal"
@@ -109,6 +110,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()
@@ -720,7 +734,87 @@ func TestUniqueBy(t *testing.T) {
actual := UniqueBy([]int{1, 2, 3, 4, 5, 6}, func(val int) int {
return val % 4
})
assert.Equal([]int{1, 2, 3, 0}, actual)
assert.Equal([]int{1, 2, 3, 4}, actual)
}
func TestUniqueByField(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUniqueByField")
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
users := []User{
{ID: 1, Name: "a"},
{ID: 2, Name: "b"},
{ID: 1, Name: "c"},
}
uniqueUsers, err := UniqueByField(users, "ID")
if err != nil {
t.Error(err)
}
assert.Equal([]User{
{ID: 1, Name: "a"},
{ID: 2, Name: "b"},
}, uniqueUsers)
}
func TestUniqueByComparator(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUniqueByComparator")
t.Run("equal comparison", func(t *testing.T) {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool {
return item == other
}
result := UniqueByComparator(nums, comparator)
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7}, result)
})
t.Run("unique struct slice by field", func(t *testing.T) {
type student struct {
Name string
Age int
}
students := []student{
{Name: "a", Age: 11},
{Name: "b", Age: 12},
{Name: "a", Age: 13},
{Name: "c", Age: 14},
}
comparator := func(item, other student) bool { return item.Name == other.Name }
result := UniqueByComparator(students, comparator)
assert.Equal([]student{
{Name: "a", Age: 11},
{Name: "b", Age: 12},
{Name: "c", Age: 14},
}, result)
})
t.Run("case-insensitive string comparison", func(t *testing.T) {
stringSlice := []string{"apple", "banana", "Apple", "cherry", "Banana", "date"}
caseInsensitiveComparator := func(item, other string) bool {
return strings.ToLower(item) == strings.ToLower(other)
}
result := UniqueByComparator(stringSlice, caseInsensitiveComparator)
assert.Equal([]string{"apple", "banana", "cherry", "date"}, result)
})
}
func TestUnion(t *testing.T) {
@@ -1357,3 +1451,68 @@ func TestSetToDefaultIf(t *testing.T) {
assert.Equal(2, count)
})
}
func TestBreak(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBreak")
// Test with integers
nums := []int{1, 2, 3, 4, 5}
even := func(n int) bool { return n%2 == 0 }
resultEven, resultAfterFirstEven := Break(nums, even)
assert.Equal([]int{1}, resultEven)
assert.Equal([]int{2, 3, 4, 5}, resultAfterFirstEven)
// Test with strings
strings := []string{"apple", "banana", "cherry", "date", "elderberry"}
startsWithA := func(s string) bool { return s[0] == 'a' }
resultStartsWithA, resultAfterFirstStartsWithA := Break(strings, startsWithA)
assert.Equal([]string{}, resultStartsWithA)
assert.Equal([]string{"apple", "banana", "cherry", "date", "elderberry"}, resultAfterFirstStartsWithA)
// Test with empty slice
emptySlice := []int{}
resultEmpty, _ := Break(emptySlice, even)
assert.Equal([]int{}, resultEmpty)
// Test with all elements satisfying the predicate
allEven := []int{2, 4, 6, 8, 10}
emptyResult, resultAllEven := Break(allEven, even)
assert.Equal([]int{2, 4, 6, 8, 10}, resultAllEven)
assert.Equal([]int{}, emptyResult)
// Test with no elements satisfying the predicate
allOdd := []int{1, 3, 5, 7, 9}
resultAllOdd, emptyResult := Break(allOdd, even)
assert.Equal([]int{1, 3, 5, 7, 9}, resultAllOdd)
assert.Equal([]int{}, emptyResult)
}
func TestRightPaddingAndLeftPadding(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "RightPaddingAndLeftPadding")
// Test with integers
nums := []int{1, 2, 3, 4, 5}
padded := LeftPadding(RightPadding(nums, 0, 3), 0, 3)
assert.Equal([]int{0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0}, padded)
}
func TestUniqueByParallel(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUniqueByParallel")
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
numOfThreads := 4
comparator := func(item int, other int) bool { return item == other }
result := UniqueByParallel(nums, numOfThreads, comparator)
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7}, result)
}

View File

@@ -585,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 {
@@ -599,7 +599,7 @@ func SubInBetween(str string, start string, end string) string {
// HammingDistance calculates the Hamming distance between two strings.
// The Hamming distance is the number of positions at which the corresponding symbols are different.
// This func returns an error if the input strings are of unequal lengths.
// Play: todo
// 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")
@@ -617,3 +617,24 @@ func HammingDistance(a, b string) (int, error) {
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

@@ -680,3 +680,17 @@ func ExampleHammingDistance() {
// 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"
@@ -583,6 +585,7 @@ func TestSubInBetween(t *testing.T) {
}
func TestHammingDistance(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "HammingDistance")
hd := func(a, b string) int {
@@ -604,3 +607,16 @@ func TestHammingDistance(t *testing.T) {
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]")
@@ -213,7 +214,7 @@ func IsIpV4(ipstr string) bool {
if ip == nil {
return false
}
return strings.Contains(ipstr, ".")
return ip.To4() != nil
}
// IsIpV6 check if the string is a ipv6 address.
@@ -223,7 +224,7 @@ func IsIpV6(ipstr string) bool {
if ip == nil {
return false
}
return strings.Contains(ipstr, ":")
return ip.To4() == nil && len(ip) == net.IPv6len
}
// IsPort check if the string is a valid net port.
@@ -264,7 +265,10 @@ func IsDns(dns string) bool {
// IsEmail check if the string is a email address.
// 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"))
}