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

Compare commits

...

30 Commits

Author SHA1 Message Date
dudaodong
3e8c3bd396 feat: add SortbyKeys for map 2024-08-23 11:21:29 +08:00
dudaodong
30971c1aab doc: rename CONTRIBUTION.md and add github start chart 2024-08-21 10:28:26 +08:00
dudaodong
bc260277bc fix: fix DecimalBytes and BinaryBytes issue #232 2024-08-19 11:36:49 +08:00
dudaodong
c0b200f846 feat: add ReduceConcurrent 2024-08-15 17:48:26 +08:00
dudaodong
305847993c feat: add ForEachConcurrent 2024-08-15 16:44:22 +08:00
dudaodong
f5d70728c3 refactoring: rename param and change its order 2024-08-15 15:50:48 +08:00
残念
c2a5335bc6 feat: add RandSliceFromGivenSlice function (#236) 2024-08-15 15:20:36 +08:00
残念
7b4e060f85 feat: add RandFromGivenSlice function (#235)
* feat: add RandFromGivenSlice function

* feat: add RandFromGivenSlice function
2024-08-14 19:36:12 +08:00
dudaodong
a360372aa9 feat: add FilterConcurrent 2024-08-14 11:19:10 +08:00
dudaodong
7f78a6b11e refactoring: rename slice_parallel to slice_concurrent 2024-08-14 10:52:36 +08:00
dudaodong
5c53cb5867 feat: add MapConcurrent 2024-08-14 10:45:35 +08:00
dudaodong
f7e9d5dc47 doc: add doc for UniqueByComparator and UniqueByParallel 2024-08-13 11:00:47 +08:00
dudaodong
5c580ed013 test: update test for Schedule 2024-08-09 15:00:19 +08:00
dudaodong
0f9764f41e feat: add Throttle function 2024-08-09 14:28:15 +08:00
dudaodong
0bc5f82554 doc: add RandBool, RandBoolSlice 2024-08-09 11:47:36 +08:00
dudaodong
e91965b013 feat: add RandStringSlice, RandBool, RandBoolSlice 2024-08-09 11:44:56 +08:00
dudaodong
483a286d8e feat: add RandIntSlice function 2024-08-09 10:46:08 +08:00
dudaodong
3f8e306ced doc: update deprecated warning text 2024-08-08 15:44:19 +08:00
dudaodong
0b29f0520d feat: add Debounce function 2024-08-08 15:19:38 +08:00
dudaodong
8611ec0c10 feat: add UniqueByComparator 2024-08-08 11:23:11 +08:00
dudaodong
286e10d189 refactoring: memory optimization for unique slice 2024-08-08 10:51:33 +08:00
dudaodong
3e7f94b03e fix: fix UniqueBy bug 2024-08-08 10:40:23 +08:00
dudaodong
356351896d feat: add UniqueByParallel for slice 2024-08-08 10:20:52 +08:00
dudaodong
9be124211e refact: preallocate in Merge map 2024-08-01 11:00:24 +08:00
dudaodong
f467658481 Merge branch 'rc' into v2 2024-07-31 10:50:29 +08:00
zyfx
5c9d0e396e Update condition.go (#234) 2024-07-31 10:16:03 +08:00
dudaodong
8f74460c1b fix: remove scientific notation in DecimalBytes 2024-07-30 14:17:17 +08:00
dudaodong
d5752499bf fix: remove scientific notation in DecimalBytes 2024-07-29 11:42:56 +08:00
dudaodong
eb7cf76eae doc: fix doc error 2024-07-18 11:44:00 +08:00
dudaodong
4af074d181 doc: add play ground demo 2024-07-18 11:34:16 +08:00
35 changed files with 2532 additions and 203 deletions

View File

@@ -1,4 +1,4 @@
# Lancet Contributing Guide
# Lancet Contribution Guide
Hi! Thank you for choosing Lancet.

View File

@@ -903,6 +903,7 @@ import "github.com/duke-git/lancet/v2/maputil"
[[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)]
@@ -1166,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.
@@ -1177,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)]
@@ -1209,11 +1210,13 @@ import "github.com/duke-git/lancet/v2/retry"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#BackoffStrategy)]
- **<big>RetryWithCustomBackoff</big>** : set abitary custom backoff strategy.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithCustomBackoff)]
[[play](https://go.dev/play/p/jIm_o2vb5Y4)]
- **<big>RetryWithLinearBackoff</big>** : set linear strategy backoff.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithLinearBackoff)]
[[play](https://go.dev/play/p/PDet2ZQZwcB)]
- **<big>RetryWithExponentialWithJitterBackoff</big>** : set exponential strategy backoff.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
[[play](https://go.dev/play/p/xp1avQmn16X)]
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1413,11 +1416,16 @@ 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>UniqueByComparator</big>** : remove duplicate elements from the input slice using the provided comparator function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByComparator)]
- **<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>UniqueByParallel</big>** : remove duplicate elements from the slice by parallel.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByParallel)]
- **<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)]
@@ -2044,11 +2052,15 @@ import "github.com/duke-git/lancet/v2/xerror"
## How to Contribute
#### [Contributing Guide](./CONTRIBUTING.md)
#### [Contribution Guide](./CONTRIBUTION.md)
## Contributors
Thank you to all the people who contributed to lancet!
<a href="https://github.com/duke-git/lancet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
</a>
</a>
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=duke-git/lancet&type=Date)](https://star-history.com/#duke-git/lancet&Date)

View File

@@ -902,9 +902,10 @@ import "github.com/duke-git/lancet/v2/maputil"
[[play](https://go.dev/play/p/N9qgYg_Ho6f)]
- **<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/isZZHOsDhFc)]
[[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)]
@@ -1168,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>** : 生成给定长度的随机符号字符串。
@@ -1209,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)]
@@ -1413,11 +1417,16 @@ 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切片去重复
- **<big>UniqueByComparator</big>** : 使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByComparator)]
- **<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>UniqueByParallel</big>** : 并发的从输入切片中移除重复元素,结果保持元素的顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByParallel)]
- **<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)]
@@ -2048,7 +2057,7 @@ import "github.com/duke-git/lancet/v2/xerror"
## 如何贡献代码
#### [贡献代码指南](./CONTRIBUTING.zh-CN.md)
#### [代码贡献指南](./CONTRIBUTION.zh-CN.md)
## 贡献者
@@ -2057,3 +2066,7 @@ import "github.com/duke-git/lancet/v2/xerror"
<a href="https://github.com/duke-git/lancet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
</a>
## GitHub Stars
[![Star History Chart](https://api.star-history.com/svg?repos=duke-git/lancet&type=Date)](https://star-history.com/#duke-git/lancet&Date)

View File

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

View File

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

View File

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

View File

@@ -56,6 +56,7 @@ import (
- [ConcurrentMap_Has](#ConcurrentMap_Has)
- [ConcurrentMap_Range](#ConcurrentMap_Range)
- [GetOrSet](#GetOrSet)
- [SortByKeys](#SortByKeys)
<div STYLE="page-break-after: always;"></div>
@@ -1497,7 +1498,7 @@ func main() {
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V
```
<b>示例:<span style="float:right;display:inline-block;"></span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/IVQwO1OkEJC)</span></b>
```go
package main
@@ -1522,4 +1523,41 @@ func main() {
// a
// b
}
```
### <span id="SortByKeys">SortByKeys</span>
<p>对传入的map根据key进行排序返回排序后的map。</p>
<b>函数签名:</b>
```go
func SortByKeys[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result := maputil.SortByKeys(m)
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
```

View File

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

View File

@@ -25,15 +25,22 @@ import (
- [RandBytes](#RandBytes)
- [RandInt](#RandInt)
- [RandString](#RandString)
- [RandFromGivenSlice](#RandFromGivenSlice)
- [RandSliceFromGivenSlice](#RandSliceFromGivenSlice)
- [RandUpper](#RandUpper)
- [RandLower](#RandLower)
- [RandNumeral](#RandNumeral)
- [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>
@@ -117,6 +124,60 @@ func main() {
}
```
### <span id="RandFromGivenSlice">RandFromGivenSlice</span>
<p>从给定切片中随机生成元素</p>
<b>函数签名:</b>
```go
func RandFromGivenSlice[T any](slice []T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
nicknames := []string{"张三", "李四", "王五", "赵六", "钱七"}
randElm := random.RandFromGivenSlice(nicknames)
fmt.Println(randElm)
}
```
### <span id="RandSliceFromGivenSlice">RandSliceFromGivenSlice</span>
<p>从给定切片中生成长度为 num 的随机切片</p>
<b>函数签名:</b>
```go
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon","mango", "nectarine", "orange"}
chosen3goods := random.RandSliceFromGivenSlice(goods, 3, false)
fmt.Println(chosen3goods)
}
```
### <span id="RandUpper">RandUpper</span>
<p>生成给定长度的随机大写字母字符串</p>
@@ -276,14 +337,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 +391,7 @@ func main() {
### <span id="RandFloat">RandFloat</span>
<p>生成随机float64数,可以指定范围和精度。</p>
<p>生成一个随机float64数,可以指定精度。数值范围[min, max)。</p>
<b>函数签名:</b>
@@ -330,7 +417,7 @@ func main() {
### <span id="RandFloats">RandFloats</span>
<p>生成随机float64数字切片,指定长度,范围和精度.</p>
<p>生成一个特定长度的随机float64切片可以指定数值精度。数值范围[min, max)。</p>
<b>函数签名:</b>
@@ -352,4 +439,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)
}
```

View File

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

View File

@@ -6,7 +6,8 @@ slice 包包含操作切片的方法集合。
## 源码:
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go](https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go)
<div STYLE="page-break-after: always;"></div>
@@ -44,6 +45,7 @@ import (
- [Equal](#Equal)
- [EqualWith](#EqualWith)
- [Filter](#Filter)
- [FilterConcurrent](#FilterConcurrent)
- [Find<sup>deprecated</sup>](#Find)
- [FindBy](#FindBy)
- [FindLast<sup>deprecated</sup>](#FindLast)
@@ -51,6 +53,7 @@ import (
- [Flatten](#Flatten)
- [FlattenDeep](#FlattenDeep)
- [ForEach](#ForEach)
- [ForEachConcurrent](#ForEachConcurrent)
- [ForEachWithBreak](#ForEachWithBreak)
- [GroupBy](#GroupBy)
- [GroupWith](#GroupWith)
@@ -61,11 +64,13 @@ import (
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [Map](#Map)
- [MapConcurrent](#MapConcurrent)
- [FilterMap](#FilterMap)
- [FlatMap](#FlatMap)
- [Merge](#Merge)
- [Reverse](#Reverse)
- [Reduce<sup>deprecated</sup>](#Reduce)
- [ReduceConcurrent](#ReduceConcurrent)
- [ReduceBy](#ReduceBy)
- [ReduceRight](#ReduceRight)
- [Replace](#Replace)
@@ -86,7 +91,9 @@ import (
- [ToSlicePointer](#ToSlicePointer)
- [Unique](#Unique)
- [UniqueBy](#UniqueBy)
- [UniqueByComparator](#UniqueByComparator)
- [UniqueByField](#UniqueByField)
- [UniqueByConcurrent](#UniqueByConcurrent)
- [Union](#Union)
- [UnionBy](#UnionBy)
- [UpdateAt](#UpdateAt)
@@ -897,10 +904,46 @@ func main() {
}
```
### <span id="Find">Find (废弃:使用 FindBy)</span>
### <span id="FilterConcurrent">FilterConcurrent</span>
<p>对slice并发执行filter操作。</p>
<b>函数签名:</b>
```go
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
result := slice.FilterConcurrent(nums, isEven, 2)
fmt.Println(result)
// Output:
// [2 4]
}
```
### <span id="Find">Find</span>
<p>遍历slice的元素返回第一个通过predicate函数真值测试的元素</p>
> ⚠️ 本函数已弃用,使用`FindBy`代替。
<b>函数签名:</b>
```go
@@ -969,10 +1012,12 @@ func main() {
}
```
### <span id="FindLast">FindLast(废弃:使用 FindLastBy)</span>
### <span id="FindLast">FindLast</span>
<p>遍历slice的元素返回最后一个通过predicate函数真值测试的元素。</p>
> ⚠️ 本函数已弃用,使用`FindLastBy`代替。
<b>函数签名:</b>
```go
@@ -1136,6 +1181,43 @@ func main() {
}
```
### <span id="ForEachConcurrent">ForEachConcurrent</span>
<p>对slice并发执行foreach操作。</p>
<b>函数签名:</b>
```go
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
result := make([]int, len(nums))
addOne := func(index int, value int) {
result[index] = value + 1
}
slice.ForEachConcurrent(nums, addOne, 4)
fmt.Println(result)
// Output:
// [2 3 4 5 6 7 8 9]
}
```
### <span id="ForEachWithBreak">ForEachWithBreak</span>
<p>遍历切片的元素并为每个元素调用iteratee函数当iteratee函数返回false时终止遍历。</p>
@@ -1244,10 +1326,12 @@ func main() {
}
```
### <span id="IntSlice">IntSlice (已弃用: 使用 go1.18+泛型代替)</span>
### <span id="IntSlice">IntSlice</span>
<p>将接口切片转换为int切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -1273,10 +1357,12 @@ func main() {
}
```
### <span id="InterfaceSlice">InterfaceSlice(已弃用: 使用 go1.18+泛型代替)</span>
### <span id="InterfaceSlice">InterfaceSlice</span>
<p>将值转换为接口切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -1441,7 +1527,7 @@ func main() {
### <span id="Map">Map</span>
<p>对slice中的每个元素执行map函数以创建一个新切片</p>
<p>对slice中的每个元素执行map函数以创建一个新切片</p>
<b>函数签名:</b>
@@ -1473,6 +1559,38 @@ func main() {
}
```
### <span id="MapConcurrent">MapConcurrent</span>
<p>对slice并发执行map操作。</p>
<b>函数签名:</b>
```go
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6}
result := slice.MapConcurrent(nums, func(_, n int) int {
return n * n
}, 4)
fmt.Println(result)
// Output:
// [1 4 9 16 25 36]
}
```
### <span id="FilterMap">FilterMap</span>
<p>返回一个将filter和map操作应用于给定切片的切片。 iteratee回调函数应该返回两个值1结果值。2结果值是否应该被包含在返回的切片中。</p>
@@ -1543,10 +1661,12 @@ func main() {
}
```
### <span id="Merge">Merge废弃使用Concat</span>
### <span id="Merge">Merge</span>
<p>合并多个切片(不会消除重复元素).</p>
> ⚠️ 本函数已弃用,使用`Concat`代替。
<b>函数签名:</b>
```go
@@ -1606,7 +1726,9 @@ func main() {
### <span id="Reduce">Reduce</span>
<p>将切片中的元素依次运行iteratee函数返回运行结果(废弃建议使用ReduceBy)</p>
<p>将切片中的元素依次运行iteratee函数返回运行结果</p>
> ⚠️ 本函数已弃用,使用`ReduceBy`代替。
<b>函数签名:</b>
@@ -1638,6 +1760,38 @@ func main() {
}
```
### <span id="ReduceConcurrent">ReduceConcurrent</span>
<p>对切片元素执行并发reduce操作。</p>
<b>函数签名:</b>
```go
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := slice.ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 1)
fmt.Println(result)
// Output:
// 55
}
```
### <span id="ReduceBy">ReduceBy</span>
<p>对切片元素执行reduce操作。</p>
@@ -2132,10 +2286,12 @@ func main() {
}
```
### <span id="StringSlice">StringSlice(已弃用: 使用 go1.18+泛型代替)</span>
### <span id="StringSlice">StringSlice</span>
<p>将接口切片转换为字符串切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -2284,12 +2440,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>
@@ -2309,7 +2465,73 @@ func main() {
fmt.Println(result)
// Output:
// [1 2 0]
// [1 2 3]
}
```
### <span id="UniqueByComparator">UniqueByComparator</span>
<p>使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。</p>
<b>函数签名:</b>
```go
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
uniqueNums := slice.UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
return item == other
})
caseInsensitiveStrings := slice.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]
}
```
### <span id="UniqueByConcurrent">UniqueByConcurrent</span>
<p>并发的从输入切片中移除重复元素,结果保持元素的顺序。</p>
<b>函数签名:</b>
```go
func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool { return item == other }
result := slice.UniqueByConcurrent(nums, comparator, 4)
fmt.Println(result)
// Output:
// [1 2 3 4 5 6 7]
}
```
@@ -2323,7 +2545,7 @@ func main() {
func UniqueByField[T any](slice []T, field string) ([]T, error)
```
<b>示例:<span style="float:right;display:inline-block;"></span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/6cifcZSPIGu)</span></b>
```go
import (

View File

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

View File

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

View File

@@ -1513,7 +1513,7 @@ func main() {
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V
```
<b>Example:<span style="float:right;display:inline-block;"></span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/IVQwO1OkEJC)</span></b>
```go
package main
@@ -1538,4 +1538,41 @@ func main() {
// a
// b
}
```
### <span id="SortByKeys">SortByKeys</span>
<p>Sorts the map by its keys and returns a new map with sorted keys.</p>
<b>Signature:</b>
```go
func SortByKeys[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
```
<b>Example:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result := maputil.SortByKeys(m)
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
```

View File

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

View File

@@ -25,15 +25,21 @@ import (
- [RandBytes](#RandBytes)
- [RandInt](#RandInt)
- [RandString](#RandString)
- [RandFromGivenSlice](#RandFromGivenSlice)
- [RandSliceFromGivenSlice](#RandSliceFromGivenSlice)
- [RandUpper](#RandUpper)
- [RandLower](#RandLower)
- [RandNumeral](#RandNumeral)
- [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>
@@ -117,6 +123,60 @@ func main() {
}
```
### <span id="RandFromGivenSlice">RandFromGivenSlice</span>
<p>Generate a random element from given slice.</p>
<b>Signature:</b>
```go
func RandFromGivenSlice[T any](slice []T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randomSet := []any{"a", 8, "hello", true, 1.1}
randElm := random.RandFromGivenSlice(randomSet)
fmt.Println(randElm)
}
```
### <span id="RandSliceFromGivenSlice">RandSliceFromGivenSlice</span>
<p>Generate a random slice of length num from given slice.</p>
<b>Signature:</b>
```go
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon", "mango", "nectarine", "orange"}
chosen3goods := random.RandSliceFromGivenSlice(goods, 3, false)
fmt.Println(chosen3goods)
}
```
### <span id="RandUpper">RandUpper</span>
<p>Generate a random upper case string</p>
@@ -276,15 +336,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 +417,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 +439,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)
}
```

View File

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

View File

@@ -6,7 +6,8 @@ Package slice implements some functions to manipulate slice.
## Source:
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go](https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go)
<div STYLE="page-break-after: always;"></div>
@@ -44,6 +45,7 @@ import (
- [EqualWith](#EqualWith)
- [Every](#Every)
- [Filter](#Filter)
- [FilterConcurrent](#FilterConcurrent)
- [Find<sup>deprecated</sup>](#Find)
- [FindBy](#FindBy)
- [FindLast<sup>deprecated</sup>](#FindLast)
@@ -51,6 +53,7 @@ import (
- [Flatten](#Flatten)
- [FlattenDeep](#FlattenDeep)
- [ForEach](#ForEach)
- [ForEachConcurrent](#ForEachConcurrent)
- [ForEachWithBreak](#ForEachWithBreak)
- [GroupBy](#GroupBy)
- [GroupWith](#GroupWith)
@@ -61,11 +64,13 @@ import (
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [Map](#Map)
- [MapConcurrent](#MapConcurrent)
- [FilterMap](#FilterMap)
- [FlatMap](#FlatMap)
- [Merge](#Merge)
- [Reverse](#Reverse)
- [Reduce<sup>deprecated</sup>](#Reduce)
- [ReduceConcurrent](#ReduceConcurrent)
- [ReduceBy](#ReduceBy)
- [ReduceRight](#ReduceRight)
- [Replace](#Replace)
@@ -86,7 +91,9 @@ import (
- [ToSlicePointer](#ToSlicePointer)
- [Unique](#Unique)
- [UniqueBy](#UniqueBy)
- [UniqueByComparator](#UniqueByComparator)
- [UniqueByField](#UniqueByField)
- [UniqueByConcurrent](#UniqueByConcurrent)
- [Union](#Union)
- [UnionBy](#UnionBy)
- [UpdateAt](#UpdateAt)
@@ -895,10 +902,46 @@ func main() {
}
```
### <span id="Find">Find(deprecated: use FindBy)</span>
### <span id="FilterConcurrent">FilterConcurrent</span>
<p>Applies the provided filter function `predicate` to each element of the input slice concurrently.</p>
<b>Signature:</b>
```go
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
result := slice.FilterConcurrent(nums, isEven, 2)
fmt.Println(result)
// Output:
// [2 4]
}
```
### <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
@@ -967,10 +1010,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
@@ -1134,6 +1179,42 @@ func main() {
}
```
### <span id="ForEachConcurrent">ForEachConcurrent</span>
<p>Applies the iteratee function to each item in the slice concurrently.</p>
<b>Signature:</b>
```go
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int)
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
result := make([]int, len(nums))
addOne := func(index int, value int) {
result[index] = value + 1
}
slice.ForEachConcurrent(nums, addOne, 4)
fmt.Println(result)
// Output:
// [2 3 4 5 6 7 8 9]
}
```
### <span id="ForEachWithBreak">ForEachWithBreak</span>
<p>Iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop.</p>
@@ -1242,10 +1323,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
@@ -1271,10 +1354,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
@@ -1471,6 +1556,36 @@ func main() {
}
```
### <span id="MapConcurrent">MapConcurrent</span>
<p>Applies the iteratee function to each item in the slice by concrrent.</p>
<b>Signature:</b>
```go
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6}
result := slice.MapConcurrent(nums, func(_, n int) int { return n * n }, 4)
fmt.Println(result)
// Output:
// [1 4 9 16 25 36]
}
```
### <span id="FilterMap">FilterMap</span>
<p>Returns a slice which apply both filtering and mapping to the given slice. iteratee callback function should returntwo values: 1, mapping result. 2, whether the result element should be included or not.</p>
@@ -1541,10 +1656,12 @@ func main() {
}
```
### <span id="Merge">Merge(deprecated: use Concat)</span>
### <span id="Merge">Merge</span>
<p>Merge all given slices into one slice.</p>
> ⚠️ This function is deprecated. use `Concat` instead.
<b>Signature:</b>
```go
@@ -1604,7 +1721,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>
@@ -1636,6 +1755,39 @@ func main() {
}
```
### <span id="ReduceConcurrent">ReduceConcurrent</span>
<p>Reduces the slice to a single value by applying the reducer function to each item in the slice concurrently.</p>
<b>Signature:</b>
```go
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T
```
<b>Example:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := slice.ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 1)
fmt.Println(result)
// Output:
// 55
}
```
### <span id="ReduceBy">ReduceBy</span>
<p>Produces a value from slice by accumulating the result of each element as passed through the reducer function.</p>
@@ -2130,10 +2282,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
@@ -2282,12 +2436,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>
@@ -2307,7 +2461,73 @@ func main() {
fmt.Println(result)
// Output:
// [1 2 0]
// [1 2 3]
}
```
### <span id="UniqueByComparator">UniqueByComparator</span>
<p>Removes duplicate elements from the input slice using the provided comparator function. The function maintains the order of the elements.</p>
<b>Signature:</b>
```go
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
uniqueNums := slice.UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
return item == other
})
caseInsensitiveStrings := slice.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]
}
```
### <span id="UniqueByConcurrent">UniqueByConcurrent</span>
<p>Removes duplicate elements from the slice by parallel.</p>
<b>Signature:</b>
```go
func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool { return item == other }
result := slice.UniqueByConcurrent(nums,comparator, 4)
fmt.Println(result)
// Output:
// [1 2 3 4 5 6 7]
}
```
@@ -2321,7 +2541,7 @@ func main() {
func UniqueByField[T any](slice []T, field string) ([]T, error)
```
<b>Example:<span style="float:right;display:inline-block;"></span></b>
<b>Example:<span style="float:right;display:inline-block;">[Runs](https://go.dev/play/p/6cifcZSPIGu)</span></b>
```go
import (
@@ -2633,14 +2853,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
}
```
@@ -2696,11 +2916,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]
}
```
@@ -2723,10 +2943,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]
}
```

View File

@@ -6,6 +6,9 @@ import (
"strconv"
"strings"
"unicode"
"github.com/duke-git/lancet/v2/mathutil"
"github.com/duke-git/lancet/v2/strutil"
)
//
@@ -84,26 +87,28 @@ 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)
return roundToToString(size, pointPosition) + unit
}
// BinaryBytes returns a human-readable byte size under binary standard (base 1024)
// The precision parameter specifies the number of digits after the decimal point, which defaults to 4.
// Play: https://go.dev/play/p/G9oHHMCAZxP
func BinaryBytes(size float64, precision ...int) string {
p := 5
pointPosition := 4
if len(precision) > 0 {
p = precision[0] + 1
pointPosition = precision[0]
}
size, unit := calculateByteSize(size, 1024.0, binaryByteUnits)
return fmt.Sprintf("%.*g%s", p, size, unit)
return roundToToString(size, pointPosition) + unit
}
func calculateByteSize(size float64, base float64, byteUnits []string) (float64, string) {
@@ -116,6 +121,29 @@ func calculateByteSize(size float64, base float64, byteUnits []string) (float64,
return size, byteUnits[i]
}
func roundToToString(x float64, max ...int) string {
pointPosition := 4
if len(max) > 0 {
pointPosition = max[0]
}
result := mathutil.RoundToString(x, pointPosition)
// 删除小数位结尾的0
decimal := strings.TrimRight(strutil.After(result, "."), "0")
if decimal == "" || pointPosition == 0 {
// 没有小数位直接返回整数
return strutil.Before(result, ".")
}
// 小数位大于想要设置的位数,按需要截断
if len(decimal) > pointPosition {
return strutil.Before(result, ".") + "." + decimal[:pointPosition]
}
// 小数位小于等于想要的位数,直接拼接返回
return strutil.Before(result, ".") + "." + decimal
}
// ParseDecimalBytes return the human readable bytes size string into the amount it represents(base 1000).
// ParseDecimalBytes("42 MB") -> 42000000, nil
// Play: https://go.dev/play/p/Am98ybWjvjj

View File

@@ -20,6 +20,14 @@ 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))
assert.Equal("4MB", DecimalBytes(4000000, 0))
assert.Equal("4MB", DecimalBytes(4000000, 1))
}
func TestBinaryBytes(t *testing.T) {
@@ -31,6 +39,10 @@ func TestBinaryBytes(t *testing.T) {
assert.Equal("1MiB", BinaryBytes(1024*1024))
assert.Equal("1.1774MiB", BinaryBytes(1234567))
assert.Equal("1.18MiB", BinaryBytes(1234567, 2))
assert.Equal("10KiB", BinaryBytes(10240, 0))
assert.Equal("10KiB", BinaryBytes(10240, 1))
assert.Equal("10KiB", BinaryBytes(10240, 2))
}
func TestParseDecimalBytes(t *testing.T) {

View File

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

View File

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

View File

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

View File

@@ -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 {
@@ -438,7 +443,7 @@ func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator fun
}
// GetOrSet returns value of the given key or set the given value value if not present.
// Play: todo
// 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
@@ -448,3 +453,23 @@ func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V {
return value
}
// SortByKeys sorts the map by its keys and returns a new map with sorted keys.
// Play: todo
func SortByKeys[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V) {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return keys[i] < keys[j]
})
sortedKeysMap = make(map[K]V, len(m))
for _, k := range keys {
sortedKeysMap[k] = m[k]
}
return
}

View File

@@ -540,3 +540,19 @@ func ExampleGetOrSet() {
// a
// b
}
func ExampleSortByKeys() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result := SortByKeys(m)
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}

View File

@@ -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)
@@ -707,3 +707,43 @@ func TestGetOrSet(t *testing.T) {
assert.Equal("a", result1)
assert.Equal("b", result2)
}
func TestSortByKeys(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSortByKeys")
m1 := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
expected1 := map[int]string{
1: "a",
2: "b",
3: "c",
4: "d",
}
result1 := SortByKeys(m1)
assert.Equal(expected1, result1)
m2 := map[string]int{
"c": 3,
"a": 1,
"d": 4,
"b": 2,
}
expected2 := map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 4,
}
result2 := SortByKeys(m2)
assert.Equal(expected2, result2)
}

View File

@@ -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,69 @@ 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
}
// RandFromGivenSlice generate a random element from given slice.
// Play: todo
func RandFromGivenSlice[T any](slice []T) T {
if len(slice) == 0 {
var zero T
return zero
}
return slice[rand.Intn(len(slice))]
}
// RandSliceFromGivenSlice generate a random slice of length num from given slice.
// - If repeatable is true, the generated slice may contain duplicate elements.
//
// Play: todo
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T {
if num <= 0 || len(slice) == 0 {
return slice
}
if !repeatable && num > len(slice) {
num = len(slice)
}
result := make([]T, num)
if repeatable {
for i := range result {
result[i] = slice[rand.Intn(len(slice))]
}
} else {
shuffled := make([]T, len(slice))
copy(shuffled, slice)
rand.Shuffle(len(shuffled), func(i, j int) {
shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
})
result = shuffled[:num]
}
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 +327,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
}

View File

@@ -43,6 +43,21 @@ func ExampleRandString() {
// 6
}
func ExampleRandFromGivenSlice() {
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon",
"mango", "nectarine", "orange"}
result := RandFromGivenSlice(goods)
fmt.Println(result)
}
func ExampleRandSliceFromGivenSlice() {
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon",
"mango", "nectarine", "orange"}
chosen3goods := RandSliceFromGivenSlice(goods, 3, false)
fmt.Println(chosen3goods)
}
func ExampleRandUpper() {
pattern := `^[A-Z]+$`
reg := regexp.MustCompile(pattern)

View File

@@ -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,167 @@ 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 TestRandFromGivenSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandFromGivenSlice")
randomSet := []any{"a", 8, "王", true, 1.1}
result := RandFromGivenSlice(randomSet)
find := false
for _, v := range randomSet {
if v == result {
find = true
}
}
assert.Equal(true, find)
emptyAnyRandomSet := []any{}
emptyAnyResult := RandFromGivenSlice(emptyAnyRandomSet)
assert.IsNil(emptyAnyResult)
emptyIntRandomSet := []int{}
emtpyIntResult := RandFromGivenSlice(emptyIntRandomSet)
assert.Equal(0, emtpyIntResult)
}
func TestRandSliceFromGivenSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandSliceFromGivenSlice")
randomSet := []any{"a", 8, "王", true, 1.1}
repeatableResult := RandSliceFromGivenSlice(randomSet, 8, true)
assert.Equal(8, len(repeatableResult))
unrepeatableResult := RandSliceFromGivenSlice(randomSet, 8, false)
assert.Equal(len(randomSet), len(unrepeatableResult))
var findCount int
for _, v := range repeatableResult {
for _, vv := range randomSet {
if v == vv {
findCount++
}
}
}
assert.Equal(8, findCount)
findCount = 0
for _, v := range unrepeatableResult {
for _, vv := range randomSet {
if v == vv {
findCount++
}
}
}
assert.Equal(len(randomSet), findCount)
emptyAnyRandomSet := []any{}
emptyAnyResult := RandSliceFromGivenSlice(emptyAnyRandomSet, 3, true)
assert.Equal([]any{}, emptyAnyResult)
emptyIntRandomSet := []int{}
emtpyIntResult := RandSliceFromGivenSlice(emptyIntRandomSet, 3, true)
assert.Equal([]int{}, emtpyIntResult)
}
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)
}
})
}

View File

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

View File

@@ -771,33 +771,69 @@ 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: todo
// Play: https://go.dev/play/p/6cifcZSPIGu
func UniqueByField[T any](slice []T, field string) ([]T, error) {
seen := map[any]struct{}{}

237
slice/slice_concurrent.go Normal file
View File

@@ -0,0 +1,237 @@
// Copyright 2024 dudaodong@gmail.com. All rights resulterved.
// Use of this source code is governed by MIT license
package slice
import (
"runtime"
"sync"
)
// ForEachConcurrent applies the iteratee function to each item in the slice concurrently.
// Play: todo
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int) {
sliceLen := len(slice)
if sliceLen == 0 {
return
}
if numThreads <= 0 {
numThreads = 1
}
var wg sync.WaitGroup
chunkSize := (sliceLen + numThreads - 1) / numThreads
for i := 0; i < numThreads; i++ {
start := i * chunkSize
end := start + chunkSize
if start >= sliceLen {
break
}
if end > sliceLen {
end = sliceLen
}
wg.Add(1)
go func(start, end int) {
defer wg.Done()
for j := start; j < end; j++ {
iteratee(j, slice[j])
}
}(start, end)
}
wg.Wait()
}
// MapConcurrent applies the iteratee function to each item in the slice concurrently.
// Play: todo
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U {
result := make([]U, len(slice))
var wg sync.WaitGroup
workerChan := make(chan struct{}, numThreads)
for index, item := range slice {
wg.Add(1)
workerChan <- struct{}{}
go func(i int, v T) {
defer wg.Done()
result[i] = iteratee(i, v)
<-workerChan
}(index, item)
}
wg.Wait()
return result
}
// ReduceConcurrent reduces the slice to a single value by applying the reducer function to each item in the slice concurrently.
// Play: todo
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T {
if numThreads <= 0 {
numThreads = 1
}
var wg sync.WaitGroup
var mu sync.Mutex
sliceLen := len(slice)
chunkSize := (sliceLen + numThreads - 1) / numThreads
results := make([]T, numThreads)
for i := 0; i < numThreads; i++ {
start := i * chunkSize
end := start + chunkSize
if end > sliceLen {
end = sliceLen
}
wg.Add(1)
go func(i, start, end int) {
defer wg.Done()
tempResult := initial
for j := start; j < end; j++ {
tempResult = reducer(j, slice[j], tempResult)
}
mu.Lock()
results[i] = tempResult
mu.Unlock()
}(i, start, end)
}
wg.Wait()
result := initial
for i, r := range results {
result = reducer(i, result, r)
}
return result
}
// FilterConcurrent applies the provided filter function `predicate` to each element of the input slice concurrently.
// Play: todo
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T {
result := make([]T, 0)
var wg sync.WaitGroup
workerChan := make(chan struct{}, numThreads)
for index, item := range slice {
wg.Add(1)
workerChan <- struct{}{}
go func(i int, v T) {
defer wg.Done()
if predicate(i, v) {
result = append(result, v)
}
<-workerChan
}(index, item)
}
wg.Wait()
return result
}
// UniqueByParallel removes duplicate elements from the slice by parallel
// The comparator function is used to compare the elements
// The numThreads parameter specifies the number of threads to use
// If numThreads 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 UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T {
if numThreads <= 0 {
numThreads = 1
} else if numThreads > len(slice) {
numThreads = len(slice)
}
maxThreads := runtime.NumCPU()
if numThreads > maxThreads {
numThreads = 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) + numThreads - 1) / numThreads
chunks := make([][]T, 0, numThreads)
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, numThreads)
var wg sync.WaitGroup
for i, chunk := range chunks {
wg.Add(1)
go func(index int, chunk []T) {
defer wg.Done()
resultCh <- resultChunk{index, removeDuplicate(chunk, comparator)}
}(i, chunk)
}
go func() {
wg.Wait()
close(resultCh)
}()
results := make([][]T, len(chunks))
for r := range resultCh {
results[r.index] = r.data
}
result := []T{}
seen := make(map[T]bool)
for _, chunk := range results {
for _, item := range chunk {
if !seen[item] {
seen[item] = true
result = append(result, item)
}
}
}
return result
}

View File

@@ -5,6 +5,7 @@ import (
"math"
"reflect"
"strconv"
"strings"
)
func ExampleContain() {
@@ -246,6 +247,21 @@ func ExampleFilter() {
// [2 4]
}
func ExampleFilterConcurrent() {
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
result := FilterConcurrent(nums, isEven, 2)
fmt.Println(result)
// Output:
// [2 4]
}
func ExampleCount() {
nums := []int{1, 2, 3, 3, 4}
@@ -413,6 +429,23 @@ func ExampleForEach() {
// [2 3 4]
}
func ExampleForEachConcurrent() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
result := make([]int, len(nums))
addOne := func(index int, value int) {
result[index] = value + 1
}
ForEachConcurrent(nums, addOne, 4)
fmt.Println(result)
// Output:
// [2 3 4 5 6 7 8 9]
}
func ExampleForEachWithBreak() {
numbers := []int{1, 2, 3, 4, 5}
@@ -494,6 +527,18 @@ func ExampleReduce() {
// 6
}
func ExampleReduceConcurrent() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 1)
fmt.Println(result)
// Output:
// 55
}
func ExampleReduceBy() {
result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int {
return agg + item
@@ -777,7 +822,24 @@ 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() {
@@ -1121,6 +1183,7 @@ func ExampleRandom() {
if idx >= 0 && idx < len(nums) && Contain(nums, val) {
fmt.Println("okk")
}
// Output:
// okk
}
@@ -1130,6 +1193,7 @@ func ExampleSetToDefaultIf() {
modifiedStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
fmt.Println(modifiedStrs)
fmt.Println(count)
// Output:
// [ b c d ]
// 3
@@ -1152,6 +1216,7 @@ func ExampleRightPadding() {
nums := []int{1, 2, 3, 4, 5}
padded := RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
}
@@ -1160,6 +1225,29 @@ func ExampleLeftPadding() {
nums := []int{1, 2, 3, 4, 5}
padded := LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
func ExampleUniqueByConcurrent() {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool { return item == other }
result := UniqueByConcurrent(nums, comparator, 4)
fmt.Println(result)
// Output:
// [1 2 3 4 5 6 7]
}
func ExampleMapConcurrent() {
nums := []int{1, 2, 3, 4, 5, 6}
result := MapConcurrent(nums, func(_, n int) int { return n * n }, 4)
fmt.Println(result)
// Output:
// [1 4 9 16 25 36]
}

View File

@@ -5,6 +5,7 @@ import (
"math"
"reflect"
"strconv"
"strings"
"testing"
"github.com/duke-git/lancet/v2/internal"
@@ -409,6 +410,73 @@ func TestForEach(t *testing.T) {
assert.Equal([]int{3, 4, 5, 6, 7}, result)
}
func TestForEachConcurrent(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestForEachConcurrent")
t.Run("single thread", func(t *testing.T) {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
result := make([]int, len(numbers))
addOne := func(index int, value int) {
result[index] = value + 1
}
ForEachConcurrent(numbers, addOne, 1)
expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10}
assert.Equal(expected, result)
})
t.Run("normal", func(t *testing.T) {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
result := make([]int, len(numbers))
addOne := func(index int, value int) {
result[index] = value + 1
}
ForEachConcurrent(numbers, addOne, 4)
expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10}
assert.Equal(expected, result)
})
t.Run("negative threads", func(t *testing.T) {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
result := make([]int, len(numbers))
addOne := func(index int, value int) {
result[index] = value + 1
}
ForEachConcurrent(numbers, addOne, -4)
expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10}
assert.Equal(expected, result)
})
t.Run("high number threads", func(t *testing.T) {
numbers := make([]int, 1000)
for i := range numbers {
numbers[i] = i
}
result := make([]int, len(numbers))
addOne := func(index int, value int) {
result[index] = value + 1
}
ForEachConcurrent(numbers, addOne, 50)
for i, item := range numbers {
assert.Equal(item+1, result[i])
}
})
}
func TestForEachWithBreak(t *testing.T) {
t.Parallel()
@@ -519,6 +587,44 @@ func TestReduce(t *testing.T) {
}
}
func TestReduceConcurrent(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestReduceConcurrent")
t.Run("basic", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 4)
assert.Equal(55, result)
})
t.Run("empty slice", func(t *testing.T) {
nums := []int{}
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 4)
assert.Equal(0, result)
})
t.Run("single thread", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 1)
assert.Equal(55, result)
})
t.Run("negative threads", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, -1)
assert.Equal(55, result)
})
}
func TestReduceBy(t *testing.T) {
t.Parallel()
@@ -733,7 +839,8 @@ 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) {
@@ -763,6 +870,58 @@ func TestUniqueByField(t *testing.T) {
}, 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) {
t.Parallel()
@@ -1448,3 +1607,68 @@ 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 TestUniqueByConcurrent(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUniqueByConcurrent")
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool { return item == other }
result := UniqueByConcurrent(nums, comparator, 4)
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7}, result)
}
func TestMapConcurrent(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMapConcurrent")
t.Run("empty slice", func(t *testing.T) {
actual := MapConcurrent([]int{}, func(_, n int) int { return n * n }, 4)
assert.Equal([]int{}, actual)
})
t.Run("single thread", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6}
expected := []int{1, 4, 9, 16, 25, 36}
actual := MapConcurrent(nums, func(_, n int) int { return n * n }, 1)
assert.Equal(expected, actual)
})
t.Run("multiple threads", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6}
expected := []int{1, 4, 9, 16, 25, 36}
actual := MapConcurrent(nums, func(_, n int) int { return n * n }, 4)
assert.Equal(expected, actual)
})
}
func TestFilterConcurrent(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFilterConcurrent")
t.Run("empty slice", func(t *testing.T) {
actual := FilterConcurrent([]int{}, func(_, n int) bool { return n != 0 }, 4)
assert.Equal([]int{}, actual)
})
t.Run("single thread", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6}
expected := []int{4, 5, 6}
actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 1)
assert.Equal(expected, actual)
})
t.Run("multiple threads", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6}
expected := []int{4, 5, 6}
actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 4)
assert.Equal(expected, actual)
})
}