mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-14 09:42:28 +08:00
Compare commits
12 Commits
f467658481
...
5c580ed013
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c580ed013 | ||
|
|
0f9764f41e | ||
|
|
0bc5f82554 | ||
|
|
e91965b013 | ||
|
|
483a286d8e | ||
|
|
3f8e306ced | ||
|
|
0b29f0520d | ||
|
|
8611ec0c10 | ||
|
|
286e10d189 | ||
|
|
3e7f94b03e | ||
|
|
356351896d | ||
|
|
9be124211e |
@@ -1167,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.
|
||||
@@ -1178,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)]
|
||||
|
||||
@@ -1416,7 +1416,7 @@ 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.
|
||||
|
||||
@@ -1169,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>** : 生成给定长度的随机符号字符串。
|
||||
@@ -1417,7 +1417,7 @@ 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切片去重复。
|
||||
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
```
|
||||
@@ -897,10 +897,12 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Find">Find (废弃:使用 FindBy)</span>
|
||||
### <span id="Find">Find</span>
|
||||
|
||||
<p>遍历slice的元素,返回第一个通过predicate函数真值测试的元素</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用`FindBy`代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -969,10 +971,12 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FindLast">FindLast(废弃:使用 FindLastBy)</span>
|
||||
### <span id="FindLast">FindLast</span>
|
||||
|
||||
<p>遍历slice的元素,返回最后一个通过predicate函数真值测试的元素。</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用`FindLastBy`代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -1244,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
|
||||
@@ -1273,10 +1279,12 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="InterfaceSlice">InterfaceSlice(已弃用: 使用 go1.18+泛型代替)</span>
|
||||
### <span id="InterfaceSlice">InterfaceSlice</span>
|
||||
|
||||
<p>将值转换为接口切片</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用go1.18+泛型代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -1543,10 +1551,12 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Merge">Merge(废弃:使用Concat)</span>
|
||||
### <span id="Merge">Merge</span>
|
||||
|
||||
<p>合并多个切片(不会消除重复元素).</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用`Concat`代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -1606,7 +1616,9 @@ func main() {
|
||||
|
||||
### <span id="Reduce">Reduce</span>
|
||||
|
||||
<p>将切片中的元素依次运行iteratee函数,返回运行结果(废弃:建议使用ReduceBy)</p>
|
||||
<p>将切片中的元素依次运行iteratee函数,返回运行结果。</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用`ReduceBy`代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -2132,10 +2144,12 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="StringSlice">StringSlice(已弃用: 使用 go1.18+泛型代替)</span>
|
||||
### <span id="StringSlice">StringSlice</span>
|
||||
|
||||
<p>将接口切片转换为字符串切片</p>
|
||||
|
||||
> ⚠️ 本函数已弃用,使用go1.18+泛型代替。
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
@@ -2284,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>
|
||||
@@ -2309,7 +2323,7 @@ func main() {
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// [1 2 0]
|
||||
// [1 2 3]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
```
|
||||
@@ -895,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
|
||||
@@ -967,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
|
||||
@@ -1242,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
|
||||
@@ -1271,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
|
||||
@@ -1541,10 +1549,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 +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>
|
||||
|
||||
@@ -2130,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
|
||||
@@ -2282,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>
|
||||
@@ -2307,7 +2321,7 @@ func main() {
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// [1 2 0]
|
||||
// [1 2 3]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -771,29 +771,65 @@ 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.
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ExampleContain() {
|
||||
@@ -777,7 +778,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() {
|
||||
@@ -1163,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
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
@@ -733,7 +734,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 +765,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 +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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user