From 3ca096b4acb0f2036d8dbf209178b76f6c2ed376 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Fri, 21 Jan 2022 14:54:55 +0800 Subject: [PATCH 1/9] update MiMeType func comment --- fileutil/file.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fileutil/file.go b/fileutil/file.go index f042914..11812a4 100644 --- a/fileutil/file.go +++ b/fileutil/file.go @@ -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 From a3399503f767fdb18a342cabea9ee982e31caa0a Mon Sep 17 00:00:00 2001 From: dudaodong Date: Fri, 21 Jan 2022 17:13:31 +0800 Subject: [PATCH 2/9] feat: add Debounced func --- function/function.go | 17 +++++++++++++++++ function/function_test.go | 22 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/function/function.go b/function/function.go index 437c34e..9171305 100644 --- a/function/function.go +++ b/function/function.go @@ -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 diff --git a/function/function_test.go b/function/function_test.go index 5ab909b..98f53f5 100644 --- a/function/function_test.go +++ b/function/function_test.go @@ -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") From aa64bf5bee28230ad88b78f577613b3112229bf3 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Sat, 22 Jan 2022 18:11:52 +0800 Subject: [PATCH 3/9] feat: add IsUrl func --- validator/validator.go | 22 ++++++++++++++++++++++ validator/validator_test.go | 10 ++++++++++ 2 files changed, 32 insertions(+) diff --git a/validator/validator.go b/validator/validator.go index bb1b8bd..362d8ec 100644 --- a/validator/validator.go +++ b/validator/validator.go @@ -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. diff --git a/validator/validator_test.go b/validator/validator_test.go index 6d07bb6..f9f41f9 100644 --- a/validator/validator_test.go +++ b/validator/validator_test.go @@ -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) { From ca88687f3de2912ee0980d9942fcefc650cbb0d4 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Sat, 22 Jan 2022 18:55:41 +0800 Subject: [PATCH 4/9] feat: add UpperFirst func --- strutil/string.go | 12 ++++++++++++ strutil/string_test.go | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/strutil/string.go b/strutil/string.go index f3b1094..268099d 100644 --- a/strutil/string.go +++ b/strutil/string.go @@ -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 { diff --git a/strutil/string_test.go b/strutil/string_test.go index ac7e35c..16432b7 100644 --- a/strutil/string_test.go +++ b/strutil/string_test.go @@ -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") From 6a1a0b8677e132fc53a828d715256833533fbf48 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Sat, 22 Jan 2022 21:16:34 +0800 Subject: [PATCH 5/9] feat: add Concat func --- slice/slice.go | 59 +++++++++++++++++++++++++++++++++++++++------ slice/slice_test.go | 53 ++++++++++++++++++++-------------------- 2 files changed, 77 insertions(+), 35 deletions(-) diff --git a/slice/slice.go b/slice/slice.go index ea9e1f8..0e978a6 100644 --- a/slice/slice.go +++ b/slice/slice.go @@ -102,21 +102,64 @@ func Chunk(slice []interface{}, size int) [][]interface{} { return res } -// Difference creates an slice of whose element not included in the other given slice -func Difference(slice1, slice2 interface{}) interface{} { - v := sliceValue(slice1) +// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey +func Compact(slice interface{}) interface{} { + sv := sliceValue(slice) var indexes []int - for i := 0; i < v.Len(); i++ { - vi := v.Index(i).Interface() - if !Contain(slice2, vi) { + for i := 0; i < sv.Len(); i++ { + item := sv.Index(i).Interface() + if item != nil && item != false && item != "" && item != 0 { + indexes = append(indexes, i) + } + } + res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes)) + for i := range indexes { + res.Index(i).Set(sv.Index(indexes[i])) + } + + return res.Interface() +} + +// Concat creates a new slice concatenating slice with any additional slices and/or values. +func Concat(slice interface{}, values ...interface{}) interface{} { + sv := sliceValue(slice) + size := sv.Len() + + res := reflect.MakeSlice(sv.Type(), size, size) + for i := 0; i < size; i++ { + res.Index(i).Set(sv.Index(i)) + } + + for _, v := range values { + if reflect.TypeOf(v).Kind() == reflect.Slice { + vv := reflect.ValueOf(v) + for i := 0; i < vv.Len(); i++ { + res = reflect.Append(res, vv.Index(i)) + } + } else { + res = reflect.Append(res, reflect.ValueOf(v)) + } + } + + return res.Interface() +} + +// Difference creates an slice of whose element in slice1, not in slice2 +func Difference(slice1, slice2 interface{}) interface{} { + sv := sliceValue(slice1) + + var indexes []int + for i := 0; i < sv.Len(); i++ { + item := sv.Index(i).Interface() + if !Contain(slice2, item) { indexes = append(indexes, i) } } - res := reflect.MakeSlice(v.Type(), len(indexes), len(indexes)) + res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes)) for i := range indexes { - res.Index(i).Set(v.Index(indexes[i])) + res.Index(i).Set(sv.Index(indexes[i])) } return res.Interface() } diff --git a/slice/slice_test.go b/slice/slice_test.go index ea452d8..9bd5b51 100644 --- a/slice/slice_test.go +++ b/slice/slice_test.go @@ -36,44 +36,43 @@ func TestChunk(t *testing.T) { assert := internal.NewAssert(t, "TestChunk") arr := []string{"a", "b", "c", "d", "e"} - r1 := [][]interface{}{ - {"a"}, - {"b"}, - {"c"}, - {"d"}, - {"e"}, - } + r1 := [][]interface{}{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}} assert.Equal(r1, Chunk(InterfaceSlice(arr), 1)) - r2 := [][]interface{}{ - {"a", "b"}, - {"c", "d"}, - {"e"}, - } + r2 := [][]interface{}{{"a", "b"}, {"c", "d"}, {"e"}} assert.Equal(r2, Chunk(InterfaceSlice(arr), 2)) - r3 := [][]interface{}{ - {"a", "b", "c"}, - {"d", "e"}, - } + r3 := [][]interface{}{{"a", "b", "c"}, {"d", "e"}} assert.Equal(r3, Chunk(InterfaceSlice(arr), 3)) - r4 := [][]interface{}{ - {"a", "b", "c", "d"}, - {"e"}, - } + r4 := [][]interface{}{{"a", "b", "c", "d"}, {"e"}} assert.Equal(r4, Chunk(InterfaceSlice(arr), 4)) - r5 := [][]interface{}{ - {"a"}, - {"b"}, - {"c"}, - {"d"}, - {"e"}, - } + r5 := [][]interface{}{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}} assert.Equal(r5, Chunk(InterfaceSlice(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 { From 92967e0add1cb3670bb5cc196a0dcf28bdd4cef8 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Sat, 22 Jan 2022 21:21:12 +0800 Subject: [PATCH 6/9] fix: TestCompact for blank string case --- slice/slice_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slice/slice_test.go b/slice/slice_test.go index 9bd5b51..82dbb61 100644 --- a/slice/slice_test.go +++ b/slice/slice_test.go @@ -58,7 +58,7 @@ func TestCompact(t *testing.T) { 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{" "}, Compact([]string{" "})) assert.Equal([]string{"a", "b", "0"}, Compact([]string{"", "a", "b", "0"})) assert.Equal([]bool{true, true}, Compact([]bool{false, true, true})) } From 6f035f710ee52e8f0311ea4574bb6191320cfd2f Mon Sep 17 00:00:00 2001 From: dudaodong Date: Sun, 23 Jan 2022 14:27:37 +0800 Subject: [PATCH 7/9] feat: add DifferenceBy func --- slice/slice.go | 39 +++++++++++++++++++++++++++++++++++++++ slice/slice_test.go | 11 +++++++++++ 2 files changed, 50 insertions(+) diff --git a/slice/slice.go b/slice/slice.go index 0e978a6..9f0b7fb 100644 --- a/slice/slice.go +++ b/slice/slice.go @@ -164,6 +164,45 @@ func Difference(slice1, slice2 interface{}) interface{} { return res.Interface() } +// 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, +// the iterateeFn function signature should be func(index int, value interface{}) interface{}. +func DifferenceBy(slice interface{}, comparedSlice interface{}, iterateeFn interface{}) interface{} { + sv := sliceValue(slice) + smv := sliceValue(comparedSlice) + fn := functionValue(iterateeFn) + + elemType := sv.Type().Elem() + if checkSliceCallbackFuncSignature(fn, elemType, nil) { + panic("function param should be of type func(" + elemType.String() + ")" + elemType.String()) + } + + slice1 := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len()) + for i := 0; i < sv.Len(); i++ { + slice1.Index(i).Set(fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]) + } + + slice2 := reflect.MakeSlice(smv.Type(), smv.Len(), smv.Len()) + for i := 0; i < smv.Len(); i++ { + slice2.Index(i).Set(fn.Call([]reflect.Value{reflect.ValueOf(i), smv.Index(i)})[0]) + } + + sliceAfterMap := slice1.Interface() + comparedSliceAfterMap := slice2.Interface() + + res := reflect.MakeSlice(sv.Type(), 0, 0) + sm := sliceValue(sliceAfterMap) + for i := 0; i < sm.Len(); i++ { + item := sm.Index(i).Interface() + if !Contain(comparedSliceAfterMap, item) { + res = reflect.Append(res, sv.Index(i)) + } + } + + return res.Interface() +} + // Every return true if all of the values in the slice pass the predicate function. // The function signature should be func(index int, value interface{}) bool . func Every(slice, function interface{}) bool { diff --git a/slice/slice_test.go b/slice/slice_test.go index 82dbb61..eec492d 100644 --- a/slice/slice_test.go +++ b/slice/slice_test.go @@ -439,6 +439,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") From e07d54d1daaa6209025a55c841689080f271bd44 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Sun, 23 Jan 2022 14:40:59 +0800 Subject: [PATCH 8/9] fix: fix exec windows command test failed --- system/os.go | 3 +++ system/os_test.go | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/system/os.go b/system/os.go index f9a290e..91a1cb9 100644 --- a/system/os.go +++ b/system/os.go @@ -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() diff --git a/system/os_test.go b/system/os_test.go index 1d5ac1f..d70813f 100644 --- a/system/os_test.go +++ b/system/os_test.go @@ -56,5 +56,7 @@ func TestExecCommand(t *testing.T) { t.Logf("error: %v\n", err) } - assert.IsNotNil(err) + if !IsWindows() { + assert.IsNotNil(err) + } } From 8bb102cb6e2046ab9ca98ee1f9295e1c2db9445f Mon Sep 17 00:00:00 2001 From: dudaodong Date: Mon, 24 Jan 2022 10:10:38 +0800 Subject: [PATCH 9/9] release v1.2.2 --- README.md | 8 +++++++- README_zh-CN.md | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3dfee18..1d10615 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

![Go version](https://img.shields.io/badge/go-%3E%3D1.16-9cf) -[![Release](https://img.shields.io/badge/release-1.2.1-green.svg)](https://github.com/duke-git/lancet/releases) +[![Release](https://img.shields.io/badge/release-1.2.2-green.svg)](https://github.com/duke-git/lancet/releases) [![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet) [![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) @@ -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(slice interface{}, value interface{}) bool //check if the value is in the slice or not func ContainSubSlice(slice interface{}, subslice interface{}) bool //check if the slice contain subslice or not func Chunk(slice []interface{}, size int) [][]interface{} //creates an slice of elements split into groups the length of `size` +func Compact(slice interface{}) interface{} //creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey +func Concat(slice interface{}, values ...interface{}) interface{} //creates a new slice concatenating slice with any additional slices and/or values func Difference(slice1, slice2 interface{}) interface{} //creates an slice of whose element not included in the other given slice +func DifferenceBy(slice interface{}, comparedSlice interface{}, iterateeFn interface{}) interface{} //it accepts iteratee which is invoked for each element of slice and values to generate the criterion by which they're compared. func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //delete the element of slice from start index to end index - 1 func Drop(slice interface{}, n int) interface{} //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(slice, function interface{}) bool //return true if all of the values in the slice pass the predicate function, function signature should be func(index int, value interface{}) bool @@ -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,5 +591,6 @@ 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) ``` diff --git a/README_zh-CN.md b/README_zh-CN.md index b9a653b..6c65093 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -4,7 +4,7 @@

![Go version](https://img.shields.io/badge/go-%3E%3D1.16-9cf) -[![Release](https://img.shields.io/badge/release-1.2.1-green.svg)](https://github.com/duke-git/lancet/releases) +[![Release](https://img.shields.io/badge/release-1.2.2-green.svg)](https://github.com/duke-git/lancet/releases) [![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet) [![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) @@ -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(slice interface{}, value interface{}) bool //判断slice是否包含value func ContainSubSlice(slice interface{}, subslice interface{}) bool //判断slice是否包含subslice func Chunk(slice []interface{}, size int) [][]interface{} //均分slice +func Compact(slice interface{}) interface{} //去除slice中的false vule. false values are false, nil, 0, and "" +func Concat(slice interface{}, values ...interface{}) interface{} //连接values到slice中 func Difference(slice1, slice2 interface{}) interface{} //返回切片,其元素在slice1中,不在slice2中 +func DifferenceBy(slice interface{}, comparedSlice interface{}, iterateeFn interface{}) interface{} //将slice 和comparedSlice中每个元素调用iterateeFn后作比较,如果不相等返回slice中的元素。 func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //删除切片中start到end位置的值 func Drop(slice interface{}, n int) interface{} //创建一个新切片,当n大于0时删除原切片前n个元素,当n小于0时删除原切片后n个元素 func Every(slice, function interface{}) bool //slice中所有元素都符合函数条件时返回true, 否则返回false. 函数签名:func(index int, value interface{}) 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,5 +592,6 @@ 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 //判断字符串是否是弱密码(只有字母或数字) ```