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

Compare commits

..

16 Commits

Author SHA1 Message Date
dudaodong
84da7d4f27 feat: add RandUpper, RandLower, RandNumeral, and RandNumeralOrLetter 2022-11-06 20:04:52 +08:00
dudaodong
089fd4e13c feat: add UnionBy for slice 2022-11-05 19:45:34 +08:00
dudaodong
6c40e02324 refactor: clean code 2022-11-05 19:33:56 +08:00
dudaodong
a270b1b634 feat: make code clear 2022-11-05 17:09:56 +08:00
dudaodong
260fb795d3 feat: add KeyBy for slice 2022-11-05 16:52:31 +08:00
dudaodong
8c036f830c refactor: remove unuseful code 2022-11-05 16:39:42 +08:00
dudaodong
bf332b9f1c release v2.1.8 2022-10-21 16:22:52 +08:00
dudaodong
6b6cd66f9f Merge branch 'main' into v2 2022-10-19 17:31:51 +08:00
燕归来
5399c2290e feat: support type alias for Number constraint (#60) 2022-10-19 17:21:53 +08:00
dudaodong
6248293c49 doc: add docment for Replace and ReplaceAll 2022-10-17 10:40:35 +08:00
dudaodong
eced25b76d test: add unit test for Replace and ReplaceAll 2022-10-17 10:26:54 +08:00
dudaodong
96a4327aa7 fix: fix some static check issues 2022-10-15 15:03:24 +08:00
dudaodong
fcfbdea597 feat: add Replace and ReplaceAll for slice 2022-10-15 14:49:49 +08:00
dudaodong
87896f917a doc: add document for pipeline 2022-10-15 12:36:11 +08:00
dudaodong
b8563ed646 feat: add Pipeline function 2022-10-15 12:29:47 +08:00
dudaodong
1ccf0af2b3 doc: update document for datetime package 2022-10-14 10:20:38 +08:00
18 changed files with 464 additions and 152 deletions

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.1.7-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.1.8-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -294,6 +294,7 @@ import "github.com/duke-git/lancet/v2/function"
- [Compose](https://github.com/duke-git/lancet/blob/main/docs/function.md#Compose)
- [Debounced](https://github.com/duke-git/lancet/blob/main/docs/function.md#Debounced)
- [Delay](https://github.com/duke-git/lancet/blob/main/docs/function.md#Delay)
- [Pipeline](https://github.com/duke-git/lancet/blob/main/docs/function.md#Pipeline)
- [Watcher](https://github.com/duke-git/lancet/blob/main/docs/function.md#Watcher)
@@ -428,6 +429,8 @@ import "github.com/duke-git/lancet/v2/slice"
- [Map](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Map)
- [Reverse](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Reverse)
- [Reduce](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Reduce)
- [Replace](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Replace)
- [ReplaceAll](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ReplaceAll)
- [Shuffle](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Shuffle)
- [SortByField](https://github.com/duke-git/lancet/blob/main/docs/slice.md#SortByField)
- [Some](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Some)

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.1.7-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.1.8-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -293,6 +293,7 @@ import "github.com/duke-git/lancet/v2/function"
- [Compose](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Compose)
- [Debounced](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Debounced)
- [Delay](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Delay)
- [Pipeline](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Pipeline)
- [Watcher](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Watcher)
@@ -423,6 +424,8 @@ import "github.com/duke-git/lancet/v2/slice"
- [Map](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Map)
- [Reverse](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Reverse)
- [Reduce](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Reduce)
- [Replace](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Replace)
- [ReplaceAll](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ReplaceAll)
- [Shuffle](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Shuffle)
- [SortByField](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#SortByField)
- [Some](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Some)

View File

@@ -251,7 +251,7 @@ func main() {
<b>Signature:</b>
```go
func BeginOfWeek(t time.Time) time.Time
func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time
```
<b>Example:</b>
@@ -414,7 +414,7 @@ func main() {
<b>Signature:</b>
```go
func EndOfWeek(t time.Time) time.Time
func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time
```
<b>Example:</b>

View File

@@ -243,12 +243,12 @@ func main() {
### <span id="BeginOfWeek">BeginOfWeek</span>
<p>返回指定时间的星期开始时间</p>
<p>返回指定时间的每周开始时间,默认开始时间星期日</p>
<b>函数签名:</b>
```go
func BeginOfWeek(t time.Time) time.Time
func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time
```
<b>例子:</b>
@@ -406,12 +406,12 @@ func main() {
### <span id="EndOfWeek">EndOfWeek</span>
<p>返回指定时间的星期结束时间</p>
<p>返回指定时间的星期结束时间,默认结束时间星期六</p>
<b>函数签名:</b>
```go
func EndOfWeek(t time.Time) time.Time
func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time
```
<b>例子:</b>

View File

@@ -26,6 +26,7 @@ import (
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Delay](#Delay)
- [Pipeline](#Pipeline)
- [Watcher](#Watcher)
<div STYLE="page-break-after: always;"></div>
@@ -300,6 +301,43 @@ func main() {
```
### <span id="Pipeline">Pipeline</span>
<p>Pipeline takes a list of functions and returns a function whose param will be passed into
the functions one by one.</p>
<b>Signature:</b>
```go
func Pipeline[T any](funcs ...func(T) T) func(T) T
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
addOne := func(x int) int {
return x + 1
}
double := func(x int) int {
return 2 * x
}
square := func(x int) int {
return x * x
}
f := Pipeline(addOne, double, square)
fmt.Println(f(2)) //36
}
```
### <span id="Watcher">Watcher</span>

View File

@@ -26,6 +26,7 @@ import (
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Delay](#Delay)
- [Pipeline](#Pipeline)
- [Watcher](#Watcher)
<div STYLE="page-break-after: always;"></div>
@@ -300,6 +301,44 @@ func main() {
### <span id="Pipeline">Pipeline</span>
<p>执行函数pipeline.</p>
<b>函数签名:</b>
```go
func Pipeline[T any](funcs ...func(T) T) func(T) T
```
<b>例子:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
addOne := func(x int) int {
return x + 1
}
double := func(x int) int {
return 2 * x
}
square := func(x int) int {
return x * x
}
f := Pipeline(addOne, double, square)
fmt.Println(f(2)) //36
}
```
### <span id="Watcher">Watcher</span>
<p>Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。 </p>

View File

@@ -53,6 +53,8 @@ import (
- [Map](#Map)
- [Reverse](#Reverse)
- [Reduce](#Reduce)
- [Replace](#Replace)
- [ReplaceAll](#ReplaceAll)
- [Shuffle](#Shuffle)
- [SortByField](#SortByField)
- [Some](#Some)
@@ -966,6 +968,60 @@ func main() {
### <span id="Replace">Replace</span>
<p>Returns a copy of the slice with the first n non-overlapping instances of old replaced by new.</p>
<b>Signature:</b>
```go
func Replace[T comparable](slice []T, old T, new T, n int) []T
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "a", "c", "d", "a"}
fmt.Println(slice.Replace(strs, "a", "x", 0)) //{"a", "b", "a", "c", "d", "a"}
fmt.Println(slice.Replace(strs, "a", "x", 1)) //{"x", "b", "a", "c", "d", "a"}
fmt.Println(slice.Replace(strs, "a", "x", -1)) //{"x", "b", "x", "c", "d", "x"}
}
```
### <span id="ReplaceAll">ReplaceAll</span>
<p>Returns a copy of the slice with the first n non-overlapping instances of old replaced by new.</p>
<b>Signature:</b>
```go
func ReplaceAll[T comparable](slice []T, old T, new T) []T
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "a", "c", "d", "a"}
fmt.Println(slice.ReplaceAll(strs, "a", "x")) //{"x", "b", "x", "c", "d", "x"}
fmt.Println(slice.Replace(strs, "e", "x")) //{"a", "b", "a", "c", "d", "a"}
}
```
### <span id="Shuffle">Shuffle</span>
<p>Creates an slice of shuffled values.</p>

View File

@@ -53,6 +53,8 @@ import (
- [Map](#Map)
- [Reverse](#Reverse)
- [Reduce](#Reduce)
- [Replace](#Replace)
- [ReplaceAll](#ReplaceAll)
- [Shuffle](#Shuffle)
- [SortByField](#SortByField)
- [Some](#Some)
@@ -966,6 +968,61 @@ func main() {
### <span id="Replace">Replace</span>
<p>返回切片的副本其中前n个不重叠的old替换为new</p>
<b>函数签名:</b>
```go
func Replace[T comparable](slice []T, old T, new T, n int) []T
```
<b>例子:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "a", "c", "d", "a"}
fmt.Println(slice.Replace(strs, "a", "x", 0)) //{"a", "b", "a", "c", "d", "a"}
fmt.Println(slice.Replace(strs, "a", "x", 1)) //{"x", "b", "a", "c", "d", "a"}
fmt.Println(slice.Replace(strs, "a", "x", -1)) //{"x", "b", "x", "c", "d", "x"}
}
```
### <span id="ReplaceAll">ReplaceAll</span>
<p>返回切片的副本将其中old全部替换为new</p>
<b>函数签名:</b>
```go
func ReplaceAll[T comparable](slice []T, old T, new T) []T
```
<b>例子:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "a", "c", "d", "a"}
fmt.Println(slice.ReplaceAll(strs, "a", "x")) //{"x", "b", "x", "c", "d", "x"}
fmt.Println(slice.Replace(strs, "e", "x")) //{"a", "b", "a", "c", "d", "a"}
}
```
### <span id="Shuffle">Shuffle</span>
<p>随机打乱切片中的元素顺序</p>

View File

@@ -113,3 +113,15 @@ func Schedule(d time.Duration, fn any, args ...any) chan bool {
return quit
}
// Pipeline takes a list of functions and returns a function whose param will be passed into
// the functions one by one.
func Pipeline[T any](funcs ...func(T) T) func(T) T {
return func(arg T) (result T) {
result = arg
for _, fn := range funcs {
result = fn(result)
}
return
}
}

View File

@@ -134,3 +134,21 @@ func TestSchedule(t *testing.T) {
// expected := []string{"*", "*", "*", "*", "*"}
// assert.Equal(expected, res)
}
func TestPipeline(t *testing.T) {
assert := internal.NewAssert(t, "TestPipeline")
addOne := func(x int) int {
return x + 1
}
double := func(x int) int {
return 2 * x
}
square := func(x int) int {
return x * x
}
f := Pipeline(addOne, double, square)
assert.Equal(36, f(2))
}

View File

@@ -29,9 +29,10 @@ func TestWatcher(t *testing.T) {
assert.Equal(int64(0), w.stopTime)
}
func longRunningTask() {
var slice []int64
func longRunningTask() []int64 {
var data []int64
for i := 0; i < 10000000; i++ {
slice = append(slice, int64(i))
data = append(data, int64(i))
}
return data
}

View File

@@ -14,5 +14,5 @@ type Comparator interface {
// Number contains all types of number and uintptr, used for generics constraint
type Number interface {
int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64
}

View File

@@ -87,6 +87,9 @@ func TestMax(t *testing.T) {
assert.Equal(Max(0, 0), 0)
assert.Equal(Max(1, 2, 3), 3)
assert.Equal(Max(1.2, 1.4, 1.1, 1.4), 1.4)
type Integer int
assert.Equal(Max(Integer(1), Integer(0)), Integer(1))
}
func TestMaxBy(t *testing.T) {

View File

@@ -12,18 +12,12 @@ import (
"time"
)
// RandString generate random string
// see https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go
func RandString(length int) string {
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
b := make([]byte, length)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := range b {
b[i] = letters[r.Int63()%int64(len(letters))]
}
return string(b)
}
const (
NUMERAL = "0123456789"
LOWER_LETTERS = "abcdefghijklmnopqrstuvwxyz"
UPPER_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)
// RandInt generate random int between min and max, maybe min, not be max
func RandInt(min, max int) int {
@@ -50,6 +44,41 @@ func RandBytes(length int) []byte {
return b
}
// RandString generate random string
func RandString(length int) string {
return random(LETTERS, length)
}
// RandUpper generate a random upper case string
func RandUpper(length int) string {
return random(UPPER_LETTERS, length)
}
// RandLower generate a random lower case string
func RandLower(length int) string {
return random(LOWER_LETTERS, length)
}
// RandNumeral generate a random numeral string
func RandNumeral(length int) string {
return random(NUMERAL, length)
}
// RandNumeralOrLetter generate a random numeral or letter string
func RandNumeralOrLetter(length int) string {
return random(NUMERAL+LETTERS, length)
}
// random generate a random string based on given string range
func random(s string, length int) string {
b := make([]byte, length)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := range b {
b[i] = s[r.Int63()%int64(len(s))]
}
return string(b)
}
// UUIdV4 generate a random UUID of version 4 according to RFC 4122
func UUIdV4() (string, error) {
uuid := make([]byte, 16)

View File

@@ -19,6 +19,51 @@ func TestRandString(t *testing.T) {
assert.Equal(true, reg.MatchString(randStr))
}
func TestRandUpper(t *testing.T) {
pattern := `^[A-Z]+$`
reg := regexp.MustCompile(pattern)
randStr := RandUpper(6)
assert := internal.NewAssert(t, "TestRandUpper")
assert.Equal(6, len(randStr))
assert.Equal(true, reg.MatchString(randStr))
}
func TestRandLower(t *testing.T) {
pattern := `^[a-z]+$`
reg := regexp.MustCompile(pattern)
randStr := RandLower(6)
assert := internal.NewAssert(t, "TestRandLower")
assert.Equal(6, len(randStr))
assert.Equal(true, reg.MatchString(randStr))
}
func TestRandNumeral(t *testing.T) {
pattern := `^[0-9]+$`
reg := regexp.MustCompile(pattern)
randStr := RandNumeral(12)
assert := internal.NewAssert(t, "TestRandNumeral")
assert.Equal(12, len(randStr))
assert.Equal(true, reg.MatchString(randStr))
}
func TestRandNumeralOrLetter(t *testing.T) {
pattern := `^[0-9a-zA-Z]+$`
reg := regexp.MustCompile(pattern)
randStr := RandNumeralOrLetter(10)
t.Log(randStr)
assert := internal.NewAssert(t, "TestRandNumeralOrLetter")
assert.Equal(10, len(randStr))
assert.Equal(true, reg.MatchString(randStr))
}
func TestRandInt(t *testing.T) {
assert := internal.NewAssert(t, "TestRandInt")

View File

@@ -36,7 +36,7 @@ func ContainSubSlice[T comparable](slice, subslice []T) bool {
// Chunk creates an slice of elements split into groups the length of size.
func Chunk[T any](slice []T, size int) [][]T {
var result [][]T
result := [][]T{}
if len(slice) == 0 || size <= 0 {
return result
@@ -69,7 +69,7 @@ func Chunk[T any](slice []T, size int) [][]T {
// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey
func Compact[T any](slice []T) []T {
result := make([]T, 0, 0)
result := make([]T, 0)
for _, v := range slice {
if !reflect.DeepEqual(v, nil) &&
!reflect.DeepEqual(v, false) &&
@@ -95,7 +95,8 @@ func Concat[T any](slice []T, values ...[]T) []T {
// Difference creates an slice of whose element in slice but not in comparedSlice
func Difference[T comparable](slice, comparedSlice []T) []T {
var result []T
result := []T{}
for _, v := range slice {
if !Contain(comparedSlice, v) {
result = append(result, v)
@@ -112,7 +113,7 @@ func DifferenceBy[T comparable](slice []T, comparedSlice []T, iteratee func(inde
orginSliceAfterMap := Map(slice, iteratee)
comparedSliceAfterMap := Map(comparedSlice, iteratee)
result := make([]T, 0, 0)
result := make([]T, 0)
for i, v := range orginSliceAfterMap {
if !Contain(comparedSliceAfterMap, v) {
result = append(result, slice[i])
@@ -123,8 +124,8 @@ func DifferenceBy[T comparable](slice []T, comparedSlice []T, iteratee func(inde
}
//DifferenceWith accepts comparator which is invoked to compare elements of slice to values. The order and references of result values are determined by the first slice. The comparator is invoked with two arguments: (arrVal, othVal).
func DifferenceWith[T any](slice []T, comparedSlice []T, comparator func(value, otherValue T) bool) []T {
result := make([]T, 0, 0)
func DifferenceWith[T any](slice []T, comparedSlice []T, comparator func(value1, value2 T) bool) []T {
result := make([]T, 0)
getIndex := func(arr []T, item T, comparison func(v1, v2 T) bool) int {
index := -1
@@ -180,11 +181,8 @@ func EqualWith[T, U any](slice1 []T, slice2 []U, comparator func(T, U) bool) boo
// Every return true if all of the values in the slice pass the predicate function.
func Every[T any](slice []T, predicate func(index int, item T) bool) bool {
if predicate == nil {
panic("predicate func is missing")
}
var currentLength int
for i, v := range slice {
if predicate(i, v) {
currentLength++
@@ -196,9 +194,6 @@ func Every[T any](slice []T, predicate func(index int, item T) bool) bool {
// None return true if all the values in the slice mismatch the criteria
func None[T any](slice []T, predicate func(index int, item T) bool) bool {
if predicate == nil {
panic("predicate func is missing")
}
var currentLength int
for i, v := range slice {
@@ -212,39 +207,30 @@ func None[T any](slice []T, predicate func(index int, item T) bool) bool {
// Some return true if any of the values in the list pass the predicate function.
func Some[T any](slice []T, predicate func(index int, item T) bool) bool {
if predicate == nil {
panic("predicate func is missing")
}
for i, v := range slice {
if predicate(i, v) {
return true
}
}
return false
}
// Filter iterates over elements of slice, returning an slice of all elements pass the predicate function
func Filter[T any](slice []T, predicate func(index int, item T) bool) []T {
if predicate == nil {
panic("predicate func is missing")
}
result := make([]T, 0, 0)
result := make([]T, 0)
for i, v := range slice {
if predicate(i, v) {
result = append(result, v)
}
}
return result
}
// Count iterates over elements of slice, returns a count of all matched elements
func Count[T any](slice []T, predicate func(index int, item T) bool) int {
if predicate == nil {
panic("predicate func is missing")
}
if len(slice) == 0 {
return 0
}
@@ -261,10 +247,6 @@ func Count[T any](slice []T, predicate func(index int, item T) bool) int {
// GroupBy iterate over elements of the slice, each element will be group by criteria, returns two slices
func GroupBy[T any](slice []T, groupFn func(index int, item T) bool) ([]T, []T) {
if groupFn == nil {
panic("groupFn func is missing")
}
if len(slice) == 0 {
return make([]T, 0), make([]T, 0)
}
@@ -285,11 +267,7 @@ func GroupBy[T any](slice []T, groupFn func(index int, item T) bool) ([]T, []T)
}
// GroupWith return a map composed of keys generated from the resultults of running each element of slice thru iteratee.
func GroupWith[T any, U comparable](slice []T, iteratee func(T) U) map[U][]T {
if iteratee == nil {
panic("iteratee func is missing")
}
func GroupWith[T any, U comparable](slice []T, iteratee func(item T) U) map[U][]T {
result := make(map[U][]T)
for _, v := range slice {
@@ -306,10 +284,6 @@ func GroupWith[T any, U comparable](slice []T, iteratee func(T) U) map[U][]T {
// Find iterates over elements of slice, returning the first one that passes a truth test on predicate function.
// If return T is nil then no items matched the predicate func
func Find[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) {
if predicate == nil {
panic("predicate func is missing")
}
if len(slice) == 0 {
return nil, false
}
@@ -332,10 +306,6 @@ func Find[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) {
// FindLast iterates over elements of slice from end to begin, returning the first one that passes a truth test on predicate function.
// If return T is nil then no items matched the predicate func
func FindLast[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) {
if predicate == nil {
panic("predicate func is missing")
}
if len(slice) == 0 {
return nil, false
}
@@ -408,10 +378,6 @@ func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value {
// ForEach iterates over elements of slice and invokes function for each element
func ForEach[T any](slice []T, iteratee func(index int, item T)) {
if iteratee == nil {
panic("iteratee func is missing")
}
for i, v := range slice {
iteratee(i, v)
}
@@ -419,10 +385,6 @@ func ForEach[T any](slice []T, iteratee func(index int, item T)) {
// Map creates an slice of values by running each element of slice thru iteratee function.
func Map[T any, U any](slice []T, iteratee func(index int, item T) U) []U {
if iteratee == nil {
panic("iteratee func is missing")
}
result := make([]U, len(slice), cap(slice))
for i, v := range slice {
result[i] = iteratee(i, v)
@@ -433,9 +395,6 @@ func Map[T any, U any](slice []T, iteratee func(index int, item T) U) []U {
// Reduce creates an slice of values by running each element of slice thru iteratee function.
func Reduce[T any](slice []T, iteratee func(index int, item1, item2 T) T, initial T) T {
if iteratee == nil {
panic("iteratee func is missing")
}
if len(slice) == 0 {
return initial
@@ -443,7 +402,7 @@ func Reduce[T any](slice []T, iteratee func(index int, item1, item2 T) T, initia
result := iteratee(0, initial, slice[0])
tmp := make([]T, 2, 2)
tmp := make([]T, 2)
for i := 1; i < len(slice); i++ {
tmp[0] = result
tmp[1] = slice[i]
@@ -453,6 +412,26 @@ func Reduce[T any](slice []T, iteratee func(index int, item1, item2 T) T, initia
return result
}
// Replace returns a copy of the slice with the first n non-overlapping instances of old replaced by new
func Replace[T comparable](slice []T, old T, new T, n int) []T {
result := make([]T, len(slice))
copy(result, slice)
for i := range result {
if result[i] == old && n != 0 {
result[i] = new
n--
}
}
return result
}
// ReplaceAll returns a copy of the slice with all non-overlapping instances of old replaced by new.
func ReplaceAll[T comparable](slice []T, old T, new T) []T {
return Replace(slice, old, new, -1)
}
// InterfaceSlice convert param to slice of interface.
func InterfaceSlice(slice any) []any {
sv := sliceValue(slice)
@@ -522,7 +501,7 @@ func DeleteAt[T any](slice []T, start int, end ...int) []T {
}
if start == size-1 {
slice = append(slice[:start])
slice = slice[:start]
} else {
slice = append(slice[:start], slice[start+1:]...)
}
@@ -584,12 +563,8 @@ func UpdateAt[T any](slice []T, index int, value T) []T {
// Unique remove duplicate elements in slice.
func Unique[T comparable](slice []T) []T {
if len(slice) == 0 {
return []T{}
}
result := []T{}
// here no use map filter. if use it, the result slice element order is random, not same as origin slice
var result []T
for i := 0; i < len(slice); i++ {
v := slice[i]
skip := true
@@ -609,11 +584,8 @@ func Unique[T comparable](slice []T) []T {
// UniqueBy call iteratee func with every item of slice, then remove duplicated.
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T {
if len(slice) == 0 {
return []T{}
}
result := []T{}
var result []T
for _, v := range slice {
val := iteratee(v)
result = append(result, val)
@@ -622,22 +594,39 @@ func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T {
return Unique(result)
}
// Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons.
// Union creates a slice of unique values, in order, from all given slices.
func Union[T comparable](slices ...[]T) []T {
if len(slices) == 0 {
return []T{}
}
// append all slices, then unique it
var allElements []T
result := []T{}
contain := map[T]struct{}{}
for _, slice := range slices {
for _, v := range slice {
allElements = append(allElements, v)
for _, item := range slice {
if _, ok := contain[item]; !ok {
contain[item] = struct{}{}
result = append(result, item)
}
}
}
return Unique(allElements)
return result
}
// UnionBy is like Union, what's more it accepts iteratee which is invoked for each element of each slice
func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T {
result := []T{}
contain := map[V]struct{}{}
for _, slice := range slices {
for _, item := range slice {
val := predicate(item)
if _, ok := contain[val]; !ok {
contain[val] = struct{}{}
result = append(result, item)
}
}
}
return result
}
// Intersection creates a slice of unique values that included by all slices.
@@ -649,8 +638,6 @@ func Intersection[T comparable](slices ...[]T) []T {
return Unique(slices[0])
}
var result []T
reducer := func(sliceA, sliceB []T) []T {
hashMap := make(map[T]int)
for _, val := range sliceA {
@@ -667,9 +654,9 @@ func Intersection[T comparable](slices ...[]T) []T {
return out
}
result = reducer(slices[0], slices[1])
result := reducer(slices[0], slices[1])
reduceSlice := make([][]T, 2, 2)
reduceSlice := make([][]T, 2)
for i := 2; i < len(slices); i++ {
reduceSlice[0] = result
reduceSlice[1] = slices[i]
@@ -800,14 +787,14 @@ func Without[T comparable](slice []T, values ...T) []T {
return slice
}
out := make([]T, 0, len(slice))
result := make([]T, 0, len(slice))
for _, v := range slice {
if !Contain(values, v) {
out = append(out, v)
result = append(result, v)
}
}
return out
return result
}
// IndexOf returns the index at which the first occurrence of a value is found in a slice or return -1
@@ -836,20 +823,20 @@ func LastIndexOf[T comparable](slice []T, value T) int {
// ToSlicePointer returns a pointer to the slices of a variable parameter transformation
func ToSlicePointer[T any](value ...T) []*T {
out := make([]*T, len(value))
result := make([]*T, len(value))
for i := range value {
out[i] = &value[i]
result[i] = &value[i]
}
return out
return result
}
// ToSlice returns a slices of a variable parameter transformation
func ToSlice[T any](value ...T) []T {
out := make([]T, len(value))
for i := range value {
out[i] = value[i]
}
return out
result := make([]T, len(value))
copy(result, value)
return result
}
// AppendIfAbsent only absent append the value
@@ -859,3 +846,15 @@ func AppendIfAbsent[T comparable](slice []T, value T) []T {
}
return slice
}
// KeyBy converts a slice to a map based on a callback function
func KeyBy[T any, U comparable](slice []T, iteratee func(item T) U) map[U]T {
result := make(map[U]T, len(slice))
for _, v := range slice {
k := iteratee(v)
result[k] = v
}
return result
}

View File

@@ -14,45 +14,6 @@ func sliceValue(slice any) reflect.Value {
return v
}
// functionValue return the reflect value of a function
func functionValue(function any) reflect.Value {
v := reflect.ValueOf(function)
if v.Kind() != reflect.Func {
panic(fmt.Sprintf("Invalid function type, value of type %T", function))
}
return v
}
// checkSliceCallbackFuncSignature Check func sign : s :[]type1{} -> func(i int, data type1) type2
// see https://coolshell.cn/articles/21164.html#%E6%B3%9B%E5%9E%8BMap-Reduce
func checkSliceCallbackFuncSignature(fn reflect.Value, types ...reflect.Type) bool {
//Check it is a function
if fn.Kind() != reflect.Func {
return false
}
// NumIn() - returns a function type's input parameter count.
// NumOut() - returns a function type's output parameter count.
if (fn.Type().NumIn() != len(types)-1) || (fn.Type().NumOut() != 1) {
return false
}
// In() - returns the type of a function type's i'th input parameter.
// first input param type should be int
if fn.Type().In(0) != reflect.TypeOf(1) {
return false
}
for i := 0; i < len(types)-1; i++ {
if fn.Type().In(i) != types[i] {
return false
}
}
// Out() - returns the type of a function type's i'th output parameter.
outType := types[len(types)-1]
if outType != nil && fn.Type().Out(0) != outType {
return false
}
return true
}
// sliceElemType get slice element type
func sliceElemType(reflectType reflect.Type) reflect.Type {
for {

View File

@@ -428,6 +428,17 @@ func TestUnion(t *testing.T) {
assert.Equal([]int{1, 3, 4, 6}, Union(s1))
}
func TestUnionBy(t *testing.T) {
assert := internal.NewAssert(t, "TestUnionBy")
testFunc := func(i int) int {
return i / 2
}
result := UnionBy(testFunc, []int{0, 1, 2, 3, 4, 5}, []int{0, 2, 10})
assert.Equal(result, []int{0, 2, 4, 10})
}
func TestIntersection(t *testing.T) {
s1 := []int{1, 2, 2, 3}
s2 := []int{1, 2, 3, 4}
@@ -617,3 +628,40 @@ func TestAppendIfAbsent(t *testing.T) {
assert.Equal([]string{"a", "b"}, AppendIfAbsent(str1, "a"))
assert.Equal([]string{"a", "b", "c"}, AppendIfAbsent(str1, "c"))
}
func TestReplace(t *testing.T) {
assert := internal.NewAssert(t, "TestReplace")
strs := []string{"a", "b", "a", "c", "d", "a"}
assert.Equal([]string{"a", "b", "a", "c", "d", "a"}, Replace(strs, "a", "x", 0))
assert.Equal([]string{"x", "b", "a", "c", "d", "a"}, Replace(strs, "a", "x", 1))
assert.Equal([]string{"x", "b", "x", "c", "d", "a"}, Replace(strs, "a", "x", 2))
assert.Equal([]string{"x", "b", "x", "c", "d", "x"}, Replace(strs, "a", "x", 3))
assert.Equal([]string{"x", "b", "x", "c", "d", "x"}, Replace(strs, "a", "x", 4))
assert.Equal([]string{"x", "b", "x", "c", "d", "x"}, Replace(strs, "a", "x", -1))
assert.Equal([]string{"x", "b", "x", "c", "d", "x"}, Replace(strs, "a", "x", -2))
assert.Equal([]string{"a", "b", "a", "c", "d", "a"}, Replace(strs, "x", "y", 1))
assert.Equal([]string{"a", "b", "a", "c", "d", "a"}, Replace(strs, "x", "y", -1))
}
func TestReplaceAll(t *testing.T) {
assert := internal.NewAssert(t, "TestReplaceAll")
strs := []string{"a", "b", "a", "c", "d", "a"}
assert.Equal([]string{"x", "b", "x", "c", "d", "x"}, ReplaceAll(strs, "a", "x"))
assert.Equal([]string{"a", "b", "a", "c", "d", "a"}, ReplaceAll(strs, "e", "x"))
}
func TestKeyBy(t *testing.T) {
assert := internal.NewAssert(t, "TestKeyBy")
result := KeyBy([]string{"a", "ab", "abc"}, func(str string) int {
return len(str)
})
assert.Equal(result, map[int]string{1: "a", 2: "ab", 3: "abc"})
}