mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 21:02:27 +08:00
merge: merge main branch and refector slice func with generics
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
</p>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -278,6 +278,7 @@ func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value //c
|
||||
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value //creates a function that invokes func once it's called less than n times
|
||||
func (f Fn) Curry(i interface{}) func(...interface{}) interface{} //make a curryed function
|
||||
func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} //compose the functions from right to left
|
||||
func Debounced(fn func(), duration time.Duration) func() //creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
|
||||
func Delay(delay time.Duration, fn interface{}, args ...interface{}) //invoke function after delayed time
|
||||
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool //invoke function every duration time, util close the returned bool chan
|
||||
func (w *Watcher) Start() //start the watch timer.
|
||||
@@ -434,7 +435,10 @@ func main() {
|
||||
func Contain[T comparable](slice []T, value T) bool //check if the value is in the slice or not
|
||||
func ContainSubSlice[T comparable](slice, subslice []T) bool //check if the slice contain subslice or not
|
||||
func Chunk[T any](slice []T, size int) [][]T //creates an slice of elements split into groups the length of size.
|
||||
func Compact[T any](slice []T) []T //creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey
|
||||
func Concat[T any](slice []T, values ...[]T) []T //creates a new slice concatenating slice with any additional slices and/or values
|
||||
func Difference[T comparable](slice1, slice2 []T) []T //creates an slice of whose element not included in the other given slice
|
||||
func DifferenceBy[T any](slice []T, comparedSlice []T, iteratee func(index int, t T) T) []T //it accepts iteratee which is invoked for each element of slice and values to generate the criterion by which they're compared.
|
||||
func DeleteByIndex[T any](slice []T, start int, end ...int) []T //delete the element of slice from start index to end index - 1
|
||||
func Drop[T any](slice []T, n int) []T //creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0
|
||||
func Every[T any](slice []T, fn func(index int, t T) bool) bool //return true if all of the values in the slice pass the predicate function
|
||||
@@ -496,6 +500,7 @@ func Capitalize(s string) string //convert the first character of a string to up
|
||||
func IsString(v interface{}) bool //check if the value data type is string or not
|
||||
func KebabCase(s string) string //covert string to kebab-case, "foo_Bar" -> "foo-bar"
|
||||
func LowerFirst(s string) string //convert the first character of string to lower case
|
||||
func UpperFirst(s string) string //converts the first character of string to upper case
|
||||
func PadEnd(source string, size int, padStr string) string //pads string on the right side if it's shorter than size
|
||||
func PadStart(source string, size int, padStr string) string//pads string on the left side if it's shorter than size
|
||||
func ReverseStr(s string) string //return string whose char order is reversed to the given string
|
||||
@@ -586,6 +591,7 @@ func IsIp(ipstr string) bool //check if the string is a ip address
|
||||
func IsIpV4(ipstr string) bool //check if the string is a ipv4 address
|
||||
func IsIpV6(ipstr string) bool //check if the string is a ipv6 address
|
||||
func IsStrongPassword(password string, length int) bool //check if the string is strong password (alpha(lower+upper) + number + special chars(!@#$%^&*()?><))
|
||||
func IsUrl(str string) bool //check if the string is url
|
||||
func IsWeakPassword(password string) bool //check if the string is weak password(only letter or only number or letter + number)
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</p>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -279,6 +279,7 @@ func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value //
|
||||
func (f Fn) Curry(i interface{}) func(...interface{}) interface{} //函数柯里化
|
||||
func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} //从右至左组合函数
|
||||
func Delay(delay time.Duration, fn interface{}, args ...interface{}) //延迟调用函数
|
||||
func Debounced(fn func(), duration time.Duration) func() //go防抖函数,在duration时间内连续调用只会执行一次.
|
||||
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool //每隔duration时间调用函数, 关闭返回通道可以停止调用
|
||||
func (w *Watcher) Start() //开时watcher
|
||||
func (w *Watcher) Stop() //开时watcher
|
||||
@@ -434,7 +435,10 @@ func main() {
|
||||
func Contain[T comparable](slice []T, value T) bool //判断slice是否包含value
|
||||
func ContainSubSlice[T comparable](slice, subslice []T) bool //判断slice是否包含subslice
|
||||
func Chunk[T any](slice []T, size int) [][]T //均分slice
|
||||
func Compact[T any](slice []T) []T //去除slice中的false vule. false values are false, nil, 0, and ""
|
||||
func Concat[T any](slice []T, values ...[]T) []T //连接values到slice中
|
||||
func Difference[T comparable](slice1, slice2 []T) []T //返回切片,其元素在slice1中,不在slice2中
|
||||
func DifferenceBy[T any](slice []T, comparedSlice []T, iteratee func(index int, t T) T) []T //将slice 和comparedSlice中每个元素调用iterateeFn后作比较,如果不相等返回slice中的元素。
|
||||
func DeleteByIndex[T any](slice []T, start int, end ...int) []T //删除切片中start到end位置的值(不包含end)
|
||||
func Drop[T any](slice []T, n int) []T //创建一个新切片,当n大于0时删除原切片前n个元素,当n小于0时删除原切片后n个元素
|
||||
func Every[T any](slice []T, fn func(index int, t T) bool) bool //slice中所有元素都符合函数条件时返回true, 否则返回false. 函数签名:func(int, t T) bool
|
||||
@@ -495,6 +499,7 @@ func CamelCase(s string) string //字符串转为cameCase, "foo bar" -> "fooBar"
|
||||
func Capitalize(s string) string //字符串转为Capitalize, "fOO" -> "Foo"
|
||||
func IsString(v interface{}) bool //判断是否是字符串
|
||||
func KebabCase(s string) string //字符串转为KebabCase, "foo_Bar" -> "foo-bar"
|
||||
func UpperFirst(s string) string //字符串的第一个字母转为大写字母
|
||||
func LowerFirst(s string) string //字符串的第一个字母转为小写字母
|
||||
func PadEnd(source string, size int, padStr string) string //字符串末尾填充size个字符
|
||||
func PadStart(source string, size int, padStr string) string//字符串开头填充size个字符
|
||||
@@ -587,6 +592,7 @@ func IsIp(ipstr string) bool //判断字符串是否是ip
|
||||
func IsIpV4(ipstr string) bool //判断字符串是否是ipv4
|
||||
func IsIpV6(ipstr string) bool //判断字符串是否是ipv6
|
||||
func IsStrongPassword(password string, length int) bool //判断字符串是否是强密码(大小写字母+数字+特殊字符)
|
||||
func IsUrl(str string) bool //判断字符串是否是url
|
||||
func IsWeakPassword(password string) bool //判断字符串是否是弱密码(只有字母或数字)
|
||||
```
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ func FileMode(path string) (fs.FileMode, error) {
|
||||
}
|
||||
|
||||
// MiMeType return file mime type
|
||||
// file should be string or *os.File
|
||||
// param `file` should be string(file path) or *os.File
|
||||
func MiMeType(file interface{}) string {
|
||||
var mediatype string
|
||||
|
||||
|
||||
@@ -70,6 +70,23 @@ func Delay(delay time.Duration, fn interface{}, args ...interface{}) {
|
||||
invokeFunc(fn, args...)
|
||||
}
|
||||
|
||||
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
|
||||
func Debounced(fn func(), duration time.Duration) func() {
|
||||
timer := time.NewTimer(duration)
|
||||
timer.Stop()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
go fn()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return func() { timer.Reset(duration) }
|
||||
}
|
||||
|
||||
// Schedule invoke function every duration time, util close the returned bool chan
|
||||
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool {
|
||||
// Catch programming error while constructing the closure
|
||||
|
||||
@@ -93,6 +93,28 @@ func TestDelay(t *testing.T) {
|
||||
Delay(2*time.Second, print, "test delay")
|
||||
}
|
||||
|
||||
func TestDebounced(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDebounced")
|
||||
|
||||
count := 0
|
||||
add := func() {
|
||||
count++
|
||||
}
|
||||
|
||||
debouncedAdd := Debounced(add, 50*time.Microsecond)
|
||||
debouncedAdd()
|
||||
debouncedAdd()
|
||||
debouncedAdd()
|
||||
debouncedAdd()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
assert.Equal(1, count)
|
||||
|
||||
debouncedAdd()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
assert.Equal(2, count)
|
||||
}
|
||||
|
||||
func TestSchedule(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSchedule")
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
)
|
||||
|
||||
// Contain check if the value is in the iterable type or not
|
||||
func Contain[T comparable](slice []T, value T) bool {
|
||||
func Contain[T any](slice []T, value T) bool {
|
||||
for _, v := range slice {
|
||||
if v == value {
|
||||
if reflect.DeepEqual(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -71,11 +71,14 @@ func Chunk[T any](slice []T, size int) [][]T {
|
||||
return res
|
||||
}
|
||||
|
||||
// Difference creates an slice of whose element in slice1 but not in slice2
|
||||
func Difference[T comparable](slice1, slice2 []T) []T {
|
||||
var res []T
|
||||
for _, v := range slice1 {
|
||||
if !Contain(slice2, v) {
|
||||
// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey
|
||||
func Compact[T any](slice []T) []T {
|
||||
res := make([]T, 0, 0)
|
||||
for _, v := range slice {
|
||||
if !reflect.DeepEqual(v, nil) &&
|
||||
!reflect.DeepEqual(v, false) &&
|
||||
!reflect.DeepEqual(v, "") &&
|
||||
!reflect.DeepEqual(v, 0) {
|
||||
res = append(res, v)
|
||||
}
|
||||
}
|
||||
@@ -83,6 +86,46 @@ func Difference[T comparable](slice1, slice2 []T) []T {
|
||||
return res
|
||||
}
|
||||
|
||||
// Concat creates a new slice concatenating slice with any additional slices and/or values.
|
||||
func Concat[T any](slice []T, values ...[]T) []T {
|
||||
res := append([]T{}, slice...)
|
||||
|
||||
for _, v := range values {
|
||||
res = append(res, v...)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Difference creates an slice of whose element in slice but not in comparedSlice
|
||||
func Difference[T comparable](slice, comparedSlice []T) []T {
|
||||
var res []T
|
||||
for _, v := range slice {
|
||||
if !Contain(comparedSlice, v) {
|
||||
res = append(res, v)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// DifferenceBy it accepts iteratee which is invoked for each element of slice
|
||||
// and values to generate the criterion by which they're compared.
|
||||
// like lodash.js differenceBy: https://lodash.com/docs/4.17.15#differenceBy,
|
||||
func DifferenceBy[T any](slice []T, comparedSlice []T, iteratee func(index int, t T) T) []T {
|
||||
orginSliceAfterMap := Map(slice, iteratee)
|
||||
comparedSliceAfterMap := Map(comparedSlice, iteratee)
|
||||
|
||||
res := make([]T, 0, 0)
|
||||
for i, v := range orginSliceAfterMap {
|
||||
if !Contain(comparedSliceAfterMap, v) {
|
||||
res = append(res, slice[i])
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Every return true if all of the values in the slice pass the predicate function.
|
||||
// The fn function signature should be func(int, T) bool .
|
||||
func Every[T any](slice []T, fn func(index int, t T) bool) bool {
|
||||
|
||||
@@ -29,6 +29,7 @@ func TestChunk(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestChunk")
|
||||
|
||||
arr := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
r1 := [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
|
||||
assert.Equal(r1, Chunk(arr, 1))
|
||||
|
||||
@@ -45,6 +46,27 @@ func TestChunk(t *testing.T) {
|
||||
assert.Equal(r5, Chunk(arr, 5))
|
||||
}
|
||||
|
||||
func TestCompact(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TesCompact")
|
||||
|
||||
assert.Equal([]int{}, Compact([]int{0}))
|
||||
assert.Equal([]int{1, 2, 3}, Compact([]int{0, 1, 2, 3}))
|
||||
assert.Equal([]string{}, Compact([]string{""}))
|
||||
assert.Equal([]string{" "}, Compact([]string{" "}))
|
||||
assert.Equal([]string{"a", "b", "0"}, Compact([]string{"", "a", "b", "0"}))
|
||||
assert.Equal([]bool{true, true}, Compact([]bool{false, true, true}))
|
||||
}
|
||||
|
||||
func TestConcat(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "Concat")
|
||||
|
||||
// assert.Equal([]int{0}, Concat([]int{}, 0))
|
||||
// assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, 4, 5))
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4, 5}))
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, []int{5}))
|
||||
// assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, 5))
|
||||
}
|
||||
|
||||
func TestEvery(t *testing.T) {
|
||||
nums := []int{1, 2, 3, 5}
|
||||
isEven := func(i, num int) bool {
|
||||
@@ -377,6 +399,17 @@ func TestDifference(t *testing.T) {
|
||||
assert.Equal([]int{1, 2, 3}, Difference(s1, s2))
|
||||
}
|
||||
|
||||
func TestDifferenceBy(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDifferenceBy")
|
||||
|
||||
s1 := []int{1, 2, 3, 4, 5} //after add one: 2 3 4 5 6
|
||||
s2 := []int{3, 4, 5} //after add one: 4 5 6
|
||||
addOne := func(i int, v int) int {
|
||||
return v + 1
|
||||
}
|
||||
assert.Equal([]int{1, 2}, DifferenceBy(s1, s2, addOne))
|
||||
}
|
||||
|
||||
func TestSortByFielDesc(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestWithout")
|
||||
|
||||
|
||||
@@ -54,6 +54,18 @@ func Capitalize(s string) string {
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// UpperFirst converts the first character of string to upper case.
|
||||
func UpperFirst(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
r, size := utf8.DecodeRuneInString(s)
|
||||
r = unicode.ToUpper(r)
|
||||
|
||||
return string(r) + s[size:]
|
||||
}
|
||||
|
||||
// LowerFirst converts the first character of string to lower case.
|
||||
func LowerFirst(s string) string {
|
||||
if len(s) == 0 {
|
||||
|
||||
@@ -50,6 +50,17 @@ func TestSnakeCase(t *testing.T) {
|
||||
assert.NotEqual("foo-bar", SnakeCase("foo_Bar"))
|
||||
}
|
||||
|
||||
func TestUpperFirst(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestLowerFirst")
|
||||
|
||||
assert.Equal("Foo", UpperFirst("foo"))
|
||||
assert.Equal("BAR", UpperFirst("bAR"))
|
||||
assert.Equal("FOo", UpperFirst("FOo"))
|
||||
assert.Equal("FOo大", UpperFirst("fOo大"))
|
||||
|
||||
assert.NotEqual("Bar", UpperFirst("BAR"))
|
||||
}
|
||||
|
||||
func TestLowerFirst(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestLowerFirst")
|
||||
|
||||
|
||||
@@ -56,6 +56,9 @@ func ExecCommand(command string) (stdout, stderr string, err error) {
|
||||
var errout bytes.Buffer
|
||||
|
||||
cmd := exec.Command("/bin/bash", "-c", command)
|
||||
if IsWindows() {
|
||||
cmd = exec.Command("cmd")
|
||||
}
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &errout
|
||||
err = cmd.Run()
|
||||
|
||||
@@ -56,5 +56,7 @@ func TestExecCommand(t *testing.T) {
|
||||
t.Logf("error: %v\n", err)
|
||||
}
|
||||
|
||||
assert.IsNotNil(err)
|
||||
if !IsWindows() {
|
||||
assert.IsNotNil(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package validator
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -123,6 +124,27 @@ func IsPort(str string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var isUrlRegexMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
|
||||
|
||||
// IsUrl check if the string is url.
|
||||
func IsUrl(str string) bool {
|
||||
if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") {
|
||||
return false
|
||||
}
|
||||
u, err := url.Parse(str)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(u.Host, ".") {
|
||||
return false
|
||||
}
|
||||
if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
|
||||
return false
|
||||
}
|
||||
|
||||
return isUrlRegexMatcher.MatchString(str)
|
||||
}
|
||||
|
||||
var isDnsRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`)
|
||||
|
||||
// IsDns check if the string is dns.
|
||||
|
||||
@@ -160,11 +160,21 @@ func TestIsIpV6(t *testing.T) {
|
||||
assert.Equal(true, IsIpV6("::0:0:0:0:0:0:1"))
|
||||
}
|
||||
|
||||
func TestIsUrl(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsUrl")
|
||||
|
||||
assert.Equal(true, IsUrl("http://abc.com"))
|
||||
assert.Equal(true, IsUrl("abc.com"))
|
||||
assert.Equal(true, IsUrl("a.b.com"))
|
||||
assert.Equal(false, IsUrl("abc"))
|
||||
}
|
||||
|
||||
func TestIsDns(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsDns")
|
||||
|
||||
assert.Equal(true, IsDns("abc.com"))
|
||||
assert.Equal(false, IsDns("a.b.com"))
|
||||
assert.Equal(false, IsDns("http://abc.com"))
|
||||
}
|
||||
|
||||
func TestIsEmail(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user