mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-16 18:52:27 +08:00
Compare commits
34 Commits
v2.3.1
...
5c580ed013
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c580ed013 | ||
|
|
0f9764f41e | ||
|
|
0bc5f82554 | ||
|
|
e91965b013 | ||
|
|
483a286d8e | ||
|
|
3f8e306ced | ||
|
|
0b29f0520d | ||
|
|
8611ec0c10 | ||
|
|
286e10d189 | ||
|
|
3e7f94b03e | ||
|
|
356351896d | ||
|
|
9be124211e | ||
|
|
f467658481 | ||
|
|
5c9d0e396e | ||
|
|
8f74460c1b | ||
|
|
d5752499bf | ||
|
|
eb7cf76eae | ||
|
|
4af074d181 | ||
|
|
73fb8fefd2 | ||
|
|
9cf535055d | ||
|
|
8be7b3e396 | ||
|
|
dac706d700 | ||
|
|
2097277a7d | ||
|
|
95b516e278 | ||
|
|
ca373b00a7 | ||
|
|
a220220f09 | ||
|
|
aeef0418a4 | ||
|
|
9b7d8d7abf | ||
|
|
4d21e81263 | ||
|
|
ce2397422e | ||
|
|
4b3a62b36a | ||
|
|
e054680d20 | ||
|
|
5381842eec | ||
|
|
6e0498514c |
28
README.md
28
README.md
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -901,10 +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)]
|
||||
@@ -1120,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.
|
||||
@@ -1159,7 +1167,7 @@ import "github.com/duke-git/lancet/v2/random"
|
||||
- **<big>UUIdV4</big>** : generate a random UUID of version 4 according to RFC 4122.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#UUIdV4)]
|
||||
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
|
||||
- **<big>RandUniqueIntSlice</big>** : generate a slice of random int of length n that do not repeat.
|
||||
- **<big>RandUniqueIntSlice</big>** : generate a slice of random int that do not repeat.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandUniqueIntSlice)]
|
||||
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
|
||||
- **<big>RandSymbolChar</big>** : Generate a random symbol char of specified length.
|
||||
@@ -1170,7 +1178,7 @@ import "github.com/duke-git/lancet/v2/random"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloat)]
|
||||
[[play](https://go.dev/play/p/zbD_tuobJtr)]
|
||||
|
||||
- **<big>RandFloats</big>** : Generate a slice of random float64 numbers of length n that do not repeat.
|
||||
- **<big>RandFloats</big>** : Generate a slice of random float64 numbers that do not repeat.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloats)]
|
||||
[[play](https://go.dev/play/p/I3yndUQ-rhh)]
|
||||
|
||||
@@ -1202,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. <a href="#index">index</a></h3>
|
||||
@@ -1406,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)]
|
||||
@@ -1440,9 +1453,10 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[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. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -903,10 +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)]
|
||||
@@ -1161,7 +1169,7 @@ import "github.com/duke-git/lancet/v2/random"
|
||||
- **<big>UUIdV4</big>** : 生成 UUID v4 字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#UUIdV4)]
|
||||
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
|
||||
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的长度为 n 的随机 int 切片。
|
||||
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的随机int切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandUniqueIntSlice)]
|
||||
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
|
||||
- **<big>RandSymbolChar</big>** : 生成给定长度的随机符号字符串。
|
||||
@@ -1202,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)]
|
||||
|
||||
|
||||
|
||||
@@ -1406,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)]
|
||||
@@ -1439,8 +1453,11 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[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)]
|
||||
|
||||
|
||||
|
||||
<h3 id="stream"> 19. stream 流,该包仅验证简单的 stream 实现,功能有限。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -40,6 +40,7 @@ export const slugify = (str: string): string =>
|
||||
export const commonConfig = defineConfig({
|
||||
title: 'Lancet',
|
||||
appearance: true,
|
||||
ignoreDeadLinks: true,
|
||||
|
||||
markdown: {
|
||||
theme: {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -40,6 +41,7 @@ import (
|
||||
- [Xnor](#Xnor)
|
||||
- [Nand](#Nand)
|
||||
- [AcceptIf](#AcceptIf)
|
||||
- [Throttle](#Throttle)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
@@ -193,9 +195,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>
|
||||
|
||||
@@ -690,4 +741,46 @@ func main() {
|
||||
// false
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### <span id="Throttle">Throttle</span>
|
||||
|
||||
<p>创建一个函数的节流版本。返回的函数保证在每个时间间隔内最多只会被调用一次。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Throttle(fn func(), interval time.Duration) func()
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
callCount := 0
|
||||
|
||||
fn := func() {
|
||||
callCount++
|
||||
}
|
||||
|
||||
throttledFn := function.Throttle(fn, 1*time.Second)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
throttledFn()
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
fmt.Println(callCount)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
```
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
- [Minus](#Minus)
|
||||
- [IsDisjoint](#IsDisjoint)
|
||||
- [HasKey](#HasKey)
|
||||
- [MapToStruct](#MapToStruct)
|
||||
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
|
||||
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
|
||||
- [NewConcurrentMap](#NewConcurrentMap)
|
||||
@@ -54,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>
|
||||
|
||||
@@ -948,14 +951,6 @@ func main() {
|
||||
|
||||
<p>检查map是否包含某个key。用于代替以下样板代码:</p>
|
||||
|
||||
```go
|
||||
_, haskey := amap["baz"];
|
||||
|
||||
if haskey {
|
||||
fmt.Println("map has key baz")
|
||||
}
|
||||
```
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -990,6 +985,49 @@ 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>
|
||||
@@ -1000,7 +1038,7 @@ func main() {
|
||||
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/43gEM2po-qy)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -1012,19 +1050,19 @@ import (
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
3: "c",
|
||||
2: "b",
|
||||
}
|
||||
1: "a",
|
||||
3: "c",
|
||||
2: "b",
|
||||
}
|
||||
|
||||
keys, values := ToSortedSlicesDefault(m)
|
||||
keys, values := ToSortedSlicesDefault(m)
|
||||
|
||||
fmt.Println(keys)
|
||||
fmt.Println(values)
|
||||
fmt.Println(keys)
|
||||
fmt.Println(values)
|
||||
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
// [a b c]
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
// [a b c]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1038,7 +1076,7 @@ func main() {
|
||||
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/0nlPo6YLdt3)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -1050,35 +1088,35 @@ import (
|
||||
|
||||
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",
|
||||
}
|
||||
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)
|
||||
})
|
||||
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 := ToSortedSlicesWithComparator(m2, func(a, b int) bool {
|
||||
return a > 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(keys2)
|
||||
fmt.Println(values2)
|
||||
|
||||
fmt.Println(keys1)
|
||||
fmt.Println(values1)
|
||||
fmt.Println(keys1)
|
||||
fmt.Println(values1)
|
||||
|
||||
// Output:
|
||||
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
|
||||
// [yesterday today tomorrow]
|
||||
// [3 2 1]
|
||||
// [c b a]
|
||||
// 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]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1144,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
|
||||
@@ -1198,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
|
||||
@@ -1296,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))
|
||||
@@ -1342,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))
|
||||
@@ -1392,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) {
|
||||
@@ -1447,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
|
||||
}
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -31,9 +31,14 @@ import (
|
||||
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
||||
- [RandSymbolChar](#RandSymbolChar)
|
||||
- [UUIdV4](#UUIdV4)
|
||||
- [RandIntSlice](#RandIntSlice)
|
||||
- [RandUniqueIntSlice](#RandUniqueIntSlice)
|
||||
- [RandFloat](#RandFloat)
|
||||
- [RandFloats](#RandFloats)
|
||||
- [RandStringSlice](#RandStringSlice)
|
||||
- [RandBool](#RandBool)
|
||||
- [RandBoolSlice](#RandBoolSlice)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -276,14 +281,40 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
|
||||
### <span id="RandIntSlice">RandIntSlice</span>
|
||||
|
||||
<p>生成一个不重复的长度为n的随机int切片。</p>
|
||||
<p>生成一个特定长度的随机int切片,数值范围[min, max)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandUniqueIntSlice(n, min, max int) []int
|
||||
func RandIntSlice(length, min, max int) []int
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandIntSlice(5, 0, 10)
|
||||
fmt.Println(result) //[1 2 7 1 5] (random)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
|
||||
|
||||
<p>生成一个特定长度的,数值不重复的随机int切片,数值范围[min, max)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandUniqueIntSlice(length, min, max int) []int
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uBkRSOz73Ec)</span></b>
|
||||
@@ -304,7 +335,7 @@ func main() {
|
||||
|
||||
### <span id="RandFloat">RandFloat</span>
|
||||
|
||||
<p>生成随机float64数字,可以指定范围和精度。</p>
|
||||
<p>生成一个随机float64数值,可以指定精度。数值范围[min, max)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -330,7 +361,7 @@ func main() {
|
||||
|
||||
### <span id="RandFloats">RandFloats</span>
|
||||
|
||||
<p>生成随机float64数字切片,指定长度,范围和精度.</p>
|
||||
<p>生成一个特定长度的随机float64切片,可以指定数值精度。数值范围[min, max)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -352,4 +383,85 @@ func main() {
|
||||
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
|
||||
fmt.Println(floatNumber) //[3.42 3.99 1.3 2.38 4.23] (random)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandStringSlice">RandStringSlice</span>
|
||||
|
||||
<p>生成随机字符串slice. 字符串类型需要是以下几种或者它们的组合: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandStringSlice(charset string, sliceLen, strLen int) []string
|
||||
```
|
||||
|
||||
<b>实例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
strs := random.RandStringSlice(random.Letters, 4, 6)
|
||||
fmt.Println(strs)
|
||||
|
||||
// output random string slice like below:
|
||||
//[CooSMq RUFjDz FAeMPf heRyGv]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandBool">RandBool</span>
|
||||
|
||||
<p>生成随机bool值(true or false)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandBool() bool
|
||||
```
|
||||
|
||||
<b>实例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandBool()
|
||||
fmt.Println(result) // true or false (random)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandBoolSlice">RandBoolSlice</span>
|
||||
|
||||
<p>生成特定长度的随机bool slice。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandBoolSlice(length int) []bool
|
||||
```
|
||||
|
||||
<b>实例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandBoolSlice(2)
|
||||
fmt.Println(result) // [true false] (random)
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -86,6 +86,7 @@ import (
|
||||
- [ToSlicePointer](#ToSlicePointer)
|
||||
- [Unique](#Unique)
|
||||
- [UniqueBy](#UniqueBy)
|
||||
- [UniqueByField](#UniqueByField)
|
||||
- [Union](#Union)
|
||||
- [UnionBy](#UnionBy)
|
||||
- [UpdateAt](#UpdateAt)
|
||||
@@ -322,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>
|
||||
@@ -896,10 +897,12 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Find">Find (废弃:使用 FindBy)</span>
|
||||
### <span id="Find">Find</span>
|
||||
|
||||
<p>遍历slice的元素,返回第一个通过predicate函数真值测试的元素</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用`FindBy`代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -968,10 +971,12 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FindLast">FindLast(废弃:使用 FindLastBy)</span>
|
||||
### <span id="FindLast">FindLast</span>
|
||||
|
||||
<p>遍历slice的元素,返回最后一个通过predicate函数真值测试的元素。</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用`FindLastBy`代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -1243,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
|
||||
@@ -1272,10 +1279,12 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="InterfaceSlice">InterfaceSlice(已弃用: 使用 go1.18+泛型代替)</span>
|
||||
### <span id="InterfaceSlice">InterfaceSlice</span>
|
||||
|
||||
<p>将值转换为接口切片</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用go1.18+泛型代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -1546,6 +1555,8 @@ func main() {
|
||||
|
||||
<p>合并多个切片(不会消除重复元素).</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用`Concat`代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -1605,7 +1616,9 @@ func main() {
|
||||
|
||||
### <span id="Reduce">Reduce</span>
|
||||
|
||||
<p>将切片中的元素依次运行iteratee函数,返回运行结果(废弃:建议使用ReduceBy)</p>
|
||||
<p>将切片中的元素依次运行iteratee函数,返回运行结果。</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用`ReduceBy`代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -2131,10 +2144,12 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="StringSlice">StringSlice(已弃用: 使用 go1.18+泛型代替)</span>
|
||||
### <span id="StringSlice">StringSlice</span>
|
||||
|
||||
<p>将接口切片转换为字符串切片</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用go1.18+泛型代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -2283,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>
|
||||
@@ -2308,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}]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2594,14 +2650,14 @@ 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
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2615,7 +2671,7 @@ func main() {
|
||||
func Break[T any](values []T, predicate func(T) bool) ([]T, []T)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/yLYcBTyeQIz)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -2648,7 +2704,7 @@ func main() {
|
||||
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/0_2rlLEMBXL)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -2657,11 +2713,11 @@ import (
|
||||
)
|
||||
|
||||
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]
|
||||
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]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2675,7 +2731,7 @@ func main() {
|
||||
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jlQVoelLl2k)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -2684,10 +2740,10 @@ import (
|
||||
)
|
||||
|
||||
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]
|
||||
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]
|
||||
}
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -40,6 +41,8 @@ import (
|
||||
- [Xnor](#Xnor)
|
||||
- [Nand](#Nand)
|
||||
- [AcceptIf](#AcceptIf)
|
||||
- [Throttle](#Throttle)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -191,11 +194,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
|
||||
@@ -689,5 +740,46 @@ func main() {
|
||||
// 0
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Throttle">Throttle</span>
|
||||
|
||||
<p>Throttle creates a throttled version of the provided function. The returned function guarantees that it will only be invoked at most once per interval.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Throttle(fn func(), interval time.Duration) func()
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
callCount := 0
|
||||
|
||||
fn := func() {
|
||||
callCount++
|
||||
}
|
||||
|
||||
throttledFn := function.Throttle(fn, 1*time.Second)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
throttledFn()
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
fmt.Println(callCount)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
```
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
- [Minus](#Minus)
|
||||
- [IsDisjoint](#IsDisjoint)
|
||||
- [HasKey](#HasKey)
|
||||
- [MapToStruct](#MapToStruct)
|
||||
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
|
||||
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
|
||||
- [NewConcurrentMap](#NewConcurrentMap)
|
||||
@@ -54,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>
|
||||
|
||||
@@ -994,6 +996,49 @@ 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>
|
||||
@@ -1005,7 +1050,7 @@ Translate the key and value of the map into two slices that are sorted in ascend
|
||||
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V)
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](Todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/43gEM2po-qy)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -1017,19 +1062,19 @@ import (
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
3: "c",
|
||||
2: "b",
|
||||
}
|
||||
1: "a",
|
||||
3: "c",
|
||||
2: "b",
|
||||
}
|
||||
|
||||
keys, values := ToSortedSlicesDefault(m)
|
||||
keys, values := maputil.ToSortedSlicesDefault(m)
|
||||
|
||||
fmt.Println(keys)
|
||||
fmt.Println(values)
|
||||
fmt.Println(keys)
|
||||
fmt.Println(values)
|
||||
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
// [a b c]
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
// [a b c]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1045,7 +1090,7 @@ Translate the key and value of the map into two slices that are sorted according
|
||||
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](Todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/0nlPo6YLdt3)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -1057,35 +1102,35 @@ import (
|
||||
|
||||
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",
|
||||
}
|
||||
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)
|
||||
})
|
||||
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 := ToSortedSlicesWithComparator(m2, func(a, b int) bool {
|
||||
return a > 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(keys2)
|
||||
fmt.Println(values2)
|
||||
|
||||
fmt.Println(keys1)
|
||||
fmt.Println(values1)
|
||||
fmt.Println(keys1)
|
||||
fmt.Println(values1)
|
||||
|
||||
// Output:
|
||||
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
|
||||
// [yesterday today tomorrow]
|
||||
// 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]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1152,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
|
||||
@@ -1207,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
|
||||
@@ -1305,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))
|
||||
@@ -1352,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))
|
||||
@@ -1404,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))
|
||||
@@ -1457,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
|
||||
}
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -31,9 +31,13 @@ import (
|
||||
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
||||
- [RandSymbolChar](#RandSymbolChar)
|
||||
- [UUIdV4](#UUIdV4)
|
||||
- [RandIntSlice](#RandIntSlice)
|
||||
- [RandUniqueIntSlice](#RandUniqueIntSlice)
|
||||
- [RandFloat](#RandFloat)
|
||||
- [RandFloats](#RandFloats)
|
||||
- [RandStringSlice](#RandStringSlice)
|
||||
- [RandBool](#RandBool)
|
||||
- [RandBoolSlice](#RandBoolSlice)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -276,15 +280,41 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandIntSlice">RandIntSlice</span>
|
||||
|
||||
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
|
||||
|
||||
<p>Generate a slice of random int of length n that do not repeat.</p>
|
||||
<p>Generate a slice of random int. Number range in [min, max)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandUniqueIntSlice(n, min, max int) []int
|
||||
func RandIntSlice(length, min, max int) []int
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandIntSlice(5, 0, 10)
|
||||
fmt.Println(result) //[1 4 7 1 5] (random)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
|
||||
|
||||
<p>Generate a slice of random int of length that do not repeat. Number range in [min, max)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandUniqueIntSlice(length, min, max int) []int
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uBkRSOz73Ec)</span></b>
|
||||
@@ -331,12 +361,12 @@ func main() {
|
||||
|
||||
### <span id="RandFloats">RandFloats</span>
|
||||
|
||||
<p>Generate a slice of random float64 numbers of length n that do not repeat.</p>
|
||||
<p>Generate a slice of random float64 numbers of length n that do not repeat. Number range in [min, max)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandFloats(n int, min, max float64, precision int) []float64
|
||||
func RandFloats(length int, min, max float64, precision int) []float64
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
|
||||
@@ -353,4 +383,86 @@ func main() {
|
||||
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
|
||||
fmt.Println(floatNumbers) //[3.42 3.99 1.3 2.38 4.23] (random)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="RandStringSlice">RandStringSlice</span>
|
||||
|
||||
<p>Generate a slice of random string of length strLen based on charset. chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars. or a combination of them.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandStringSlice(charset string, sliceLen, strLen int) []string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
strs := random.RandStringSlice(random.Letters, 4, 6)
|
||||
fmt.Println(strs)
|
||||
|
||||
// output random string slice like below:
|
||||
//[CooSMq RUFjDz FAeMPf heRyGv]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandBool">RandBool</span>
|
||||
|
||||
<p>Generate a random boolean value (true or false).</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandBool() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandBool()
|
||||
fmt.Println(result) // true or false (random)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandBoolSlice">RandBoolSlice</span>
|
||||
|
||||
<p>Generates a random boolean slice of specified length.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandBoolSlice(length int) []bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandBoolSlice(2)
|
||||
fmt.Println(result) // [true false] (random)
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -86,6 +86,7 @@ import (
|
||||
- [ToSlicePointer](#ToSlicePointer)
|
||||
- [Unique](#Unique)
|
||||
- [UniqueBy](#UniqueBy)
|
||||
- [UniqueByField](#UniqueByField)
|
||||
- [Union](#Union)
|
||||
- [UnionBy](#UnionBy)
|
||||
- [UpdateAt](#UpdateAt)
|
||||
@@ -321,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>
|
||||
@@ -894,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
|
||||
@@ -966,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
|
||||
@@ -1241,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
|
||||
@@ -1270,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
|
||||
@@ -1544,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
|
||||
@@ -1603,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>
|
||||
|
||||
@@ -2129,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
|
||||
@@ -2281,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>
|
||||
@@ -2306,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}]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2612,7 +2668,7 @@ func main() {
|
||||
func Break[T any](values []T, predicate func(T) bool) ([]T, []T)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/yLYcBTyeQIz)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -2645,7 +2701,7 @@ func main() {
|
||||
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/0_2rlLEMBXL)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -2655,7 +2711,7 @@ import (
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
padded := RightPadding(nums, 0, 3)
|
||||
padded := slice.RightPadding(nums, 0, 3)
|
||||
fmt.Println(padded)
|
||||
// Output:
|
||||
// [1 2 3 4 5 0 0 0]
|
||||
@@ -2672,7 +2728,7 @@ func main() {
|
||||
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jlQVoelLl2k)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -2682,7 +2738,7 @@ import (
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
padded := LeftPadding(nums, 0, 3)
|
||||
padded := slice.LeftPadding(nums, 0, 3)
|
||||
fmt.Println(padded)
|
||||
// Output:
|
||||
// [0 0 0 1 2 3 4 5]
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
1171
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,6 @@
|
||||
"docs:preview": "vitepress preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitepress": "^1.0.0-rc.4"
|
||||
"vitepress": "^1.2.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -7,6 +7,7 @@ package function
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -84,36 +85,97 @@ 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
|
||||
}
|
||||
|
||||
// Throttle creates a throttled version of the provided function.
|
||||
// The returned function guarantees that it will only be invoked at most once per interval.
|
||||
// Play: todo
|
||||
func Throttle(fn func(), interval time.Duration) func() {
|
||||
var (
|
||||
timer *time.Timer
|
||||
lastRun time.Time
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
return func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
if now.Sub(lastRun) >= interval {
|
||||
fn()
|
||||
lastRun = now
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
timer = nil
|
||||
}
|
||||
} else if timer == nil {
|
||||
delay := interval - now.Sub(lastRun)
|
||||
|
||||
timer = time.AfterFunc(delay, func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
fn()
|
||||
lastRun = time.Now()
|
||||
timer = nil
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule invoke function every duration time, util close the returned bool channel.
|
||||
// Play: https://go.dev/play/p/hbON-Xeyn5N
|
||||
func Schedule(d time.Duration, fn any, args ...any) chan bool {
|
||||
func Schedule(duration time.Duration, fn any, args ...any) chan bool {
|
||||
// Catch programming error while constructing the closure
|
||||
mustBeFunction(fn)
|
||||
|
||||
quit := make(chan bool)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
unsafeInvokeFunc(fn, args...)
|
||||
|
||||
select {
|
||||
case <-time.After(d):
|
||||
case <-time.After(duration):
|
||||
case <-quit:
|
||||
return
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
@@ -174,3 +200,24 @@ func ExampleAcceptIf() {
|
||||
// 0
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleThrottle() {
|
||||
callCount := 0
|
||||
|
||||
fn := func() {
|
||||
callCount++
|
||||
}
|
||||
|
||||
throttledFn := Throttle(fn, 1*time.Second)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
throttledFn()
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
fmt.Println(callCount)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
|
||||
@@ -125,24 +125,178 @@ 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
|
||||
|
||||
debouncedFn, _ := Debounce(func() {
|
||||
callCount++
|
||||
}, 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
|
||||
|
||||
debouncedFn, _ := Debounce(func() {
|
||||
callCount++
|
||||
}, 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
|
||||
|
||||
debouncedFn, _ := Debounce(func() {
|
||||
callCount++
|
||||
}, 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
|
||||
|
||||
debouncedFn, cancelFn := Debounce(func() {
|
||||
callCount++
|
||||
}, 500*time.Millisecond)
|
||||
|
||||
debouncedFn()
|
||||
|
||||
cancelFn()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
assert.Equal(0, callCount)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestThrottle(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestThrottle")
|
||||
|
||||
t.Run("Single call", func(t *testing.T) {
|
||||
callCount := 0
|
||||
|
||||
throttledFn := Throttle(func() {
|
||||
callCount++
|
||||
}, 1*time.Second)
|
||||
|
||||
throttledFn()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
assert.Equal(1, callCount)
|
||||
})
|
||||
|
||||
t.Run("Multiple calls within throttle interval", func(t *testing.T) {
|
||||
callCount := 0
|
||||
|
||||
throttledFn := Throttle(func() {
|
||||
callCount++
|
||||
}, 1*time.Second)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
throttledFn()
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
assert.Equal(1, callCount)
|
||||
})
|
||||
|
||||
t.Run("Mutiple calls space out throttle interval", func(t *testing.T) {
|
||||
callCount := 0
|
||||
|
||||
throttledFn := Throttle(func() {
|
||||
callCount++
|
||||
}, 500*time.Millisecond)
|
||||
|
||||
throttledFn()
|
||||
time.Sleep(600 * time.Millisecond)
|
||||
|
||||
throttledFn()
|
||||
time.Sleep(600 * time.Millisecond)
|
||||
|
||||
throttledFn()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
assert.Equal(3, callCount)
|
||||
})
|
||||
|
||||
t.Run("Call function near the end of the interval", func(t *testing.T) {
|
||||
callCount := 0
|
||||
|
||||
throttledFn := Throttle(func() {
|
||||
callCount++
|
||||
}, 1*time.Second)
|
||||
|
||||
throttledFn()
|
||||
time.Sleep(900 * time.Millisecond)
|
||||
|
||||
throttledFn()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
assert.Equal(2, callCount)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSchedule(t *testing.T) {
|
||||
// assert := internal.NewAssert(t, "TestSchedule")
|
||||
assert := internal.NewAssert(t, "TestSchedule")
|
||||
|
||||
var res []string
|
||||
appendStr := func(s string) {
|
||||
res = append(res, s)
|
||||
}
|
||||
t.Run("Single call", func(t *testing.T) {
|
||||
res := []string{}
|
||||
appendStr := func(s string) {
|
||||
res = append(res, s)
|
||||
}
|
||||
stop := Schedule(200*time.Millisecond, appendStr, "*")
|
||||
close(stop)
|
||||
|
||||
stop := Schedule(1*time.Second, appendStr, "*")
|
||||
time.Sleep(5 * time.Second)
|
||||
close(stop)
|
||||
time.Sleep(400 * time.Millisecond)
|
||||
|
||||
t.Log(res)
|
||||
assert.Equal([]string{"*"}, res)
|
||||
})
|
||||
|
||||
t.Run("Multiple calls", func(t *testing.T) {
|
||||
res := []string{}
|
||||
appendStr := func(s string) {
|
||||
res = append(res, s)
|
||||
}
|
||||
stop := Schedule(200*time.Millisecond, appendStr, "*")
|
||||
|
||||
time.Sleep(800 * time.Millisecond)
|
||||
|
||||
close(stop)
|
||||
|
||||
assert.Equal([]string{"*", "*", "*", "*"}, res)
|
||||
})
|
||||
|
||||
// todo: in github action, for now, this test is not working sometimes
|
||||
// res maybe [* * * * *] or [* * * * * *]
|
||||
// expected := []string{"*", "*", "*", "*", "*"}
|
||||
// assert.Equal(expected, res)
|
||||
}
|
||||
|
||||
func TestPipeline(t *testing.T) {
|
||||
|
||||
@@ -70,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 {
|
||||
@@ -310,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)
|
||||
@@ -389,6 +394,7 @@ func getFieldNameByJsonTag(structObj any, jsonTag string) string {
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
@@ -413,6 +419,7 @@ func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V) {
|
||||
|
||||
// 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))
|
||||
|
||||
@@ -434,3 +441,15 @@ func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator fun
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -524,3 +524,19 @@ func ExampleToSortedSlicesWithComparator() {
|
||||
// [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
|
||||
}
|
||||
|
||||
@@ -107,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)
|
||||
|
||||
@@ -691,3 +691,19 @@ func TestToSortedSlicesWithComparator(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ type HttpRequest struct {
|
||||
|
||||
// HttpClientConfig contains some configurations for http client
|
||||
type HttpClientConfig struct {
|
||||
Timeout time.Duration
|
||||
SSLEnabled bool
|
||||
TLSConfig *tls.Config
|
||||
Compressed bool
|
||||
@@ -113,9 +114,10 @@ type HttpClientConfig struct {
|
||||
|
||||
// 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,
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
148
random/random.go
148
random/random.go
@@ -23,6 +23,7 @@ const (
|
||||
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
||||
AllChars = Numeral + LowwerLetters + UpperLetters + SymbolChars
|
||||
)
|
||||
|
||||
var rn = rand.NewSource(time.Now().UnixNano())
|
||||
@@ -31,6 +32,27 @@ func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// RandBool generates a random boolean value (true or false).
|
||||
// Play: todo
|
||||
func RandBool() bool {
|
||||
return rand.Intn(2) == 1
|
||||
}
|
||||
|
||||
// RandBoolSlice generates a random boolean slice of specified length.
|
||||
// Play: todo
|
||||
func RandBoolSlice(length int) []bool {
|
||||
if length <= 0 {
|
||||
return []bool{}
|
||||
}
|
||||
|
||||
result := make([]bool, length)
|
||||
for i := range result {
|
||||
result[i] = RandBool()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// RandInt generate random int between [min, max).
|
||||
// Play: https://go.dev/play/p/pXyyAAI5YxD
|
||||
func RandInt(min, max int) int {
|
||||
@@ -42,9 +64,54 @@ func RandInt(min, max int) int {
|
||||
min, max = max, min
|
||||
}
|
||||
|
||||
if min == 0 && max == math.MaxInt {
|
||||
return rand.Int()
|
||||
}
|
||||
|
||||
return rand.Intn(max-min) + min
|
||||
}
|
||||
|
||||
// RandIntSlice generates a slice of random integers.
|
||||
// The generated integers are between min and max (exclusive).
|
||||
// Play: todo
|
||||
func RandIntSlice(length, min, max int) []int {
|
||||
if length <= 0 || min > max {
|
||||
return []int{}
|
||||
}
|
||||
|
||||
result := make([]int, length)
|
||||
for i := range result {
|
||||
result[i] = RandInt(min, max)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// RandUniqueIntSlice generate a slice of random int of length that do not repeat.
|
||||
// Play: https://go.dev/play/p/uBkRSOz73Ec
|
||||
func RandUniqueIntSlice(length, min, max int) []int {
|
||||
if min > max {
|
||||
return []int{}
|
||||
}
|
||||
if length > max-min {
|
||||
length = max - min
|
||||
}
|
||||
|
||||
nums := make([]int, length)
|
||||
used := make(map[int]struct{}, length)
|
||||
for i := 0; i < length; {
|
||||
r := RandInt(min, max)
|
||||
if _, use := used[r]; use {
|
||||
continue
|
||||
}
|
||||
used[r] = struct{}{}
|
||||
nums[i] = r
|
||||
i++
|
||||
}
|
||||
|
||||
return nums
|
||||
}
|
||||
|
||||
// RandFloat generate random float64 number between [min, max) with specific precision.
|
||||
// Play: https://go.dev/play/p/zbD_tuobJtr
|
||||
func RandFloat(min, max float64, precision int) float64 {
|
||||
@@ -61,6 +128,24 @@ func RandFloat(min, max float64, precision int) float64 {
|
||||
return mathutil.RoundToFloat(n, precision)
|
||||
}
|
||||
|
||||
// RandFloats generate a slice of random float64 numbers of length that do not repeat.
|
||||
// Play: https://go.dev/play/p/I3yndUQ-rhh
|
||||
func RandFloats(length int, min, max float64, precision int) []float64 {
|
||||
nums := make([]float64, length)
|
||||
used := make(map[float64]struct{}, length)
|
||||
for i := 0; i < length; {
|
||||
r := RandFloat(min, max, precision)
|
||||
if _, use := used[r]; use {
|
||||
continue
|
||||
}
|
||||
used[r] = struct{}{}
|
||||
nums[i] = r
|
||||
i++
|
||||
}
|
||||
|
||||
return nums
|
||||
}
|
||||
|
||||
// RandBytes generate random byte slice.
|
||||
// Play: https://go.dev/play/p/EkiLESeXf8d
|
||||
func RandBytes(length int) []byte {
|
||||
@@ -76,12 +161,30 @@ func RandBytes(length int) []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
// RandString generate random alpha string of specified length.
|
||||
// RandString generate random alphabeta string of specified length.
|
||||
// Play: https://go.dev/play/p/W2xvRUXA7Mi
|
||||
func RandString(length int) string {
|
||||
return random(Letters, length)
|
||||
}
|
||||
|
||||
// RandString generate a slice of random string of length strLen based on charset.
|
||||
// chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters
|
||||
// random.Letters, random.SymbolChars, random.AllChars. or a combination of them.
|
||||
// Play: todo
|
||||
func RandStringSlice(charset string, sliceLen, strLen int) []string {
|
||||
if sliceLen <= 0 || strLen <= 0 {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
result := make([]string, sliceLen)
|
||||
|
||||
for i := range result {
|
||||
result[i] = random(charset, strLen)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// RandUpper generate a random upper case string of specified length.
|
||||
// Play: https://go.dev/play/p/29QfOh0DVuh
|
||||
func RandUpper(length int) string {
|
||||
@@ -185,46 +288,3 @@ func UUIdV4() (string, error) {
|
||||
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
|
||||
}
|
||||
|
||||
// RandUniqueIntSlice generate a slice of random int of length n that do not repeat.
|
||||
// Play: https://go.dev/play/p/uBkRSOz73Ec
|
||||
func RandUniqueIntSlice(n, min, max int) []int {
|
||||
if min > max {
|
||||
return []int{}
|
||||
}
|
||||
if n > max-min {
|
||||
n = max - min
|
||||
}
|
||||
|
||||
nums := make([]int, n)
|
||||
used := make(map[int]struct{}, n)
|
||||
for i := 0; i < n; {
|
||||
r := RandInt(min, max)
|
||||
if _, use := used[r]; use {
|
||||
continue
|
||||
}
|
||||
used[r] = struct{}{}
|
||||
nums[i] = r
|
||||
i++
|
||||
}
|
||||
|
||||
return nums
|
||||
}
|
||||
|
||||
// RandFloats generate a slice of random float64 numbers of length n that do not repeat.
|
||||
// Play: https://go.dev/play/p/I3yndUQ-rhh
|
||||
func RandFloats(n int, min, max float64, precision int) []float64 {
|
||||
nums := make([]float64, n)
|
||||
used := make(map[float64]struct{}, n)
|
||||
for i := 0; i < n; {
|
||||
r := RandFloat(min, max, precision)
|
||||
if _, use := used[r]; use {
|
||||
continue
|
||||
}
|
||||
used[r] = struct{}{}
|
||||
nums[i] = r
|
||||
i++
|
||||
}
|
||||
|
||||
return nums
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func TestRandString(t *testing.T) {
|
||||
@@ -196,3 +197,104 @@ func TestRandFloats(t *testing.T) {
|
||||
|
||||
assert.Equal(len(numbers), 5)
|
||||
}
|
||||
|
||||
func TestRandIntSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestRandIntSlice")
|
||||
|
||||
t.Run("empty slice", func(t *testing.T) {
|
||||
numbers := RandIntSlice(-1, 1, 5)
|
||||
assert.Equal([]int{}, numbers)
|
||||
|
||||
numbers = RandIntSlice(0, 1, 5)
|
||||
assert.Equal([]int{}, numbers)
|
||||
|
||||
numbers = RandIntSlice(3, 5, 1)
|
||||
assert.Equal([]int{}, numbers)
|
||||
})
|
||||
|
||||
t.Run("random int slice", func(t *testing.T) {
|
||||
numbers := RandIntSlice(5, 1, 1)
|
||||
assert.Equal([]int{1, 1, 1, 1, 1}, numbers)
|
||||
|
||||
numbers = RandIntSlice(5, 1, 5)
|
||||
assert.Equal(5, len(numbers))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRandStringSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestRandStringSlice")
|
||||
|
||||
t.Run("empty slice", func(t *testing.T) {
|
||||
strs := RandStringSlice(Letters, -1, -1)
|
||||
assert.Equal([]string{}, strs)
|
||||
|
||||
strs = RandStringSlice(Letters, 0, 0)
|
||||
assert.Equal([]string{}, strs)
|
||||
|
||||
strs = RandStringSlice(Letters, -1, 0)
|
||||
assert.Equal([]string{}, strs)
|
||||
|
||||
strs = RandStringSlice(Letters, 0, -1)
|
||||
assert.Equal([]string{}, strs)
|
||||
|
||||
strs = RandStringSlice(Letters, 1, 0)
|
||||
assert.Equal([]string{}, strs)
|
||||
|
||||
strs = RandStringSlice(Letters, 0, 1)
|
||||
assert.Equal([]string{}, strs)
|
||||
})
|
||||
|
||||
t.Run("random string slice", func(t *testing.T) {
|
||||
strs := RandStringSlice(Letters, 4, 6)
|
||||
assert.Equal(4, len(strs))
|
||||
|
||||
for _, s := range strs {
|
||||
assert.Equal(true, validator.IsAlpha(s))
|
||||
assert.Equal(6, len(s))
|
||||
}
|
||||
})
|
||||
|
||||
// fail test: chinese character is not supported for now
|
||||
// t.Run("random string slice of chinese ", func(t *testing.T) {
|
||||
// strs := RandStringSlice("你好你好你好你好你好你好你好你好你好", 4, 6)
|
||||
// t.Log(strs)
|
||||
|
||||
// assert.Equal(4, len(strs))
|
||||
// for _, s := range strs {
|
||||
// assert.Equal(true, validator.ContainChinese(s))
|
||||
// assert.Equal(6, len(s))
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
func TestRandBool(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestRandBool")
|
||||
|
||||
result := RandBool()
|
||||
assert.Equal(true, result == true || result == false)
|
||||
}
|
||||
|
||||
func TestRandBoolSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestRandBoolSlice")
|
||||
|
||||
t.Run("empty slice", func(t *testing.T) {
|
||||
bools := RandBoolSlice(-1)
|
||||
assert.Equal([]bool{}, bools)
|
||||
|
||||
bools = RandBoolSlice(0)
|
||||
assert.Equal([]bool{}, bools)
|
||||
})
|
||||
|
||||
t.Run("random bool slice", func(t *testing.T) {
|
||||
bools := RandBoolSlice(6)
|
||||
assert.Equal(6, len(bools))
|
||||
|
||||
for _, b := range bools {
|
||||
assert.Equal(true, b == true || b == false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
137
slice/slice.go
137
slice/slice.go
@@ -109,18 +109,18 @@ func Compact[T comparable](slice []T) []T {
|
||||
|
||||
// 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 {
|
||||
totalLen := len(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)
|
||||
|
||||
result = append(result, slice...)
|
||||
for _, s := range slices {
|
||||
result = append(result, s...)
|
||||
for _, v := range slices {
|
||||
result = append(result, v...)
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -771,29 +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{}
|
||||
exists := map[T]bool{}
|
||||
for _, t := range slice {
|
||||
if exists[t] {
|
||||
result := make([]T, 0, len(slice))
|
||||
seen := make(map[T]struct{}, len(slice))
|
||||
|
||||
for i := range slice {
|
||||
if _, ok := seen[slice[i]]; ok {
|
||||
continue
|
||||
}
|
||||
exists[t] = true
|
||||
result = append(result, t)
|
||||
|
||||
seen[slice[i]] = struct{}{}
|
||||
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
|
||||
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{}
|
||||
// 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 _, v := range slice {
|
||||
val := iteratee(v)
|
||||
result = append(result, val)
|
||||
for i := range slice {
|
||||
key := iteratee(slice[i])
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
seen[key] = struct{}{}
|
||||
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
|
||||
return Unique(result)
|
||||
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 !duplicate {
|
||||
seen = append(seen, item)
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 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{}{}
|
||||
|
||||
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 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.
|
||||
@@ -833,20 +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 {
|
||||
totalLen := 0
|
||||
for _, v := range slices {
|
||||
totalLen += len(v)
|
||||
}
|
||||
result := make([]T, 0, totalLen)
|
||||
|
||||
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.
|
||||
@@ -1254,7 +1321,7 @@ func Partition[T any](slice []T, predicates ...func(item T) bool) [][]T {
|
||||
}
|
||||
|
||||
// Breaks a list into two parts at the point where the predicate for the first time is true.
|
||||
// Play: Todo
|
||||
// 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)
|
||||
@@ -1289,7 +1356,7 @@ func Random[T any](slice []T) (val T, idx int) {
|
||||
}
|
||||
|
||||
// RightPadding adds padding to the right end of a slice.
|
||||
// Play: Todo
|
||||
// Play: https://go.dev/play/p/0_2rlLEMBXL
|
||||
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
||||
if paddingLength == 0 {
|
||||
return slice
|
||||
@@ -1301,7 +1368,7 @@ func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
||||
}
|
||||
|
||||
// LeftPadding adds padding to the left begin of a slice.
|
||||
// Play: Todo
|
||||
// Play: https://go.dev/play/p/jlQVoelLl2k
|
||||
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
||||
if paddingLength == 0 {
|
||||
return slice
|
||||
|
||||
@@ -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() {
|
||||
@@ -1141,3 +1181,15 @@ func ExampleLeftPadding() {
|
||||
// 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
96
slice/slice_parallel.go
Normal 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
|
||||
}
|
||||
@@ -2,11 +2,13 @@ package slice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestContain(t *testing.T) {
|
||||
@@ -732,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) {
|
||||
@@ -1420,3 +1502,17 @@ func TestRightPaddingAndLeftPadding(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -214,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.
|
||||
@@ -224,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.
|
||||
|
||||
Reference in New Issue
Block a user