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

Compare commits

...

21 Commits

Author SHA1 Message Date
dudaodong
56fc2aabd6 fmt: fix lint issue 2021-12-28 10:08:08 +08:00
dudaodong
0ddd52225b Merge branch 'main' of github.com:duke-git/lancet into main 2021-12-28 10:07:33 +08:00
donutloop
3919160e38 Optimized: Capitalize (#4)
* Use unicode.ToUpper func to convert first letter in string to upper case
letter.

* Preallocate memory with correct length and cap because the final
  string is not changing.
2021-12-28 10:04:15 +08:00
dudaodong
40ec5bc0f6 fmt: fix lint issue 2021-12-27 20:40:54 +08:00
dudaodong
99faeccb05 feat: add Intersection, Union, Without func for slice/slice.go 2021-12-27 19:54:04 +08:00
dudaodong
ad777bc877 feat: add Intersection, Union, Without func for slice/slice.go 2021-12-27 19:47:45 +08:00
dudaodong
589ce7404f refactor: update painc message 2021-12-27 15:12:18 +08:00
dudaodong
6ab4fca433 fmt: go fmt fileutil/file_test.go and function/function.go 2021-12-26 21:41:46 +08:00
dudaodong
b33a9cbfe3 release v1.0.10 2021-12-26 21:20:18 +08:00
donutloop
9ad9d1805b Regex validators: Precompile all known regex patterns (#3)
Reduce executions time of pattern matching, regex patterns are expensive to
compile at runtime.
2021-12-26 21:16:20 +08:00
dudaodong
0a1386f5a7 release v1.0.9 2021-12-26 13:57:05 +08:00
Beyond
b5541ea177 Merge pull request #2 from donutloop/slice/Some
Some: allocate slice to keep track of unused indexes is not necessary
2021-12-26 11:44:46 +08:00
Marcel Edmund Franke
578b1bba65 Some: allocate slice to keep track unused indexes is not necessary
Waste of memory for those unused indexes. It got replaced by a simple
boolean to keep track of it.
2021-12-25 13:04:03 +01:00
dudaodong
3045d56503 release: update readme.file v1.0.8, ParseHttpResponse in netutil/request.go 2021-12-20 11:43:39 +08:00
dudaodong
f1dbd943aa refactor: rename ParseResponse to ParseHttpResponse func in netutil/request.go 2021-12-17 17:29:28 +08:00
dudaodong
e87f3b70f0 feat: add ParseResponse func in netutil/request.go 2021-12-17 17:23:18 +08:00
dudaodong
26b59dd56b update: update go test command in workflows/codecov.yml 2021-12-14 11:02:19 +08:00
dudaodong
143aba7112 release: update readme file, new feature for functional programming 2021-12-14 10:55:07 +08:00
dudaodong
60f3a72c88 refactor: update Compose func in function.go 2021-12-14 10:29:53 +08:00
dudaodong
d1b74cfcfb feat: add Compose in function.go 2021-12-13 20:15:34 +08:00
dudaodong
72a89be8c1 feat: add function package for funcational programming 2021-12-11 13:30:11 +08:00
13 changed files with 545 additions and 70 deletions

View File

@@ -17,6 +17,6 @@ jobs:
with:
go-version: "1.16"
- name: Run coverage
run: go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic
- name: Upload coverage to Codecov
run: bash <(curl -s https://codecov.io/bash)

View File

@@ -6,7 +6,7 @@
<div align="center" style="text-align: center;">
![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf)
[![Release](https://img.shields.io/badge/release-1.0.6-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-1.1.0-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)
[![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet)
@@ -246,7 +246,39 @@ func main() {
func Comma(v interface{}, symbol string) string //add comma to number by every 3 numbers from right. ahead by symbol char
```
#### 6. netutil is for net process
#### 6. function can control the function execution and support functional programming
- Control function execution and support functional programming.
- Usage: import "github.com/duke-git/lancet/function"
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/function"
)
func main() {
var print = func(s string) {
fmt.Println(s)
}
function.Delay(2*time.Second, print, "hello world")
}
```
- Function list:
```go
func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value //creates a function that invokes func once it's called n or more times
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 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
```
#### 7. netutil is for net process
- Ip and http request method.
- Usage: import "github.com/duke-git/lancet/netutil".
@@ -291,9 +323,10 @@ func HttpPut(url string, params ...interface{}) (*http.Response, error) //http p
func HttpDelete(url string, params ...interface{}) (*http.Response, error) //http delete request
func HttpPatch(url string, params ...interface{}) (*http.Response, error) //http patch request
func ConvertMapToQueryString(param map[string]interface{}) string //convert map to url query string
func ParseHttpResponse(resp *http.Response, obj interface{}) error //decode http response to specified interface
```
#### 7. random is for rand string and int generation
#### 8. random is for rand string and int generation
- Generate random string and int.
- Usage: import "github.com/duke-git/lancet/random".
@@ -322,7 +355,7 @@ func RandInt(min, max int) int //generate random int
func RandString(length int) string //generate random string
```
#### 8. slice is for process slice
#### 9. slice is for process slice
- Contain function for process slice.
- Usage: import "github.com/duke-git/lancet/slice"
@@ -358,6 +391,7 @@ func Filter(slice, function interface{}) interface{} //filter slice, function si
func Find(slice, function interface{}) interface{} //iterates over elements of slice, returning the first one that passes a truth test on function.function signature should be func(index int, value interface{}) bool .
func IntSlice(slice interface{}) ([]int, error) //convert value to int slice
func InterfaceSlice(slice interface{}) []interface{} //convert value to interface{} slice
func Intersection(slices ...interface{}) interface{} //creates a slice of unique values that included by all slices.
func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //insert the element into slice at index.
func Map(slice, function interface{}) interface{} //map lisce, function signature should be func(index int, value interface{}) interface{}
func ReverseSlice(slice interface{}) //revere slice
@@ -366,10 +400,12 @@ func SortByField(slice interface{}, field string, sortType ...string) error //so
func Some(slice, function interface{}) bool //return true if any of the values in the list pass the predicate function, function signature should be func(index int, value interface{}) bool
func StringSlice(slice interface{}) []string //convert value to string slice
func Unique(slice interface{}) interface{} //remove duplicate elements in slice
func Union(slices ...interface{}) interface{} //Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons.
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //update the slice element at index.
func Without(slice interface{}, values ...interface{}) interface{} //creates a slice excluding all given values
```
#### 9. strutil is for processing string
#### 10. strutil is for processing string
- Contain functions to precess string
- Usage: import "github.com/duke-git/lancet/strutil"
@@ -409,7 +445,7 @@ func ReverseStr(s string) string //return string whose char order is reversed to
func SnakeCase(s string) string //covert string to snake_case "fooBar" -> "foo_bar"
```
#### 10. validator is for data validation
#### 11. validator is for data validation
- Contain function for data validation.
- Usage: import "github.com/duke-git/lancet/validator".

View File

@@ -6,7 +6,7 @@
<div align="center" style="text-align: center;">
![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf)
[![Release](https://img.shields.io/badge/release-1.0.6-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-1.1.0-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)
[![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet)
@@ -247,7 +247,39 @@ func main() {
func Comma(v interface{}, symbol string) string //用逗号每隔3位分割数字/字符串
```
#### 6. netutil网络处理包
#### 6. function包可以控制函数执行支持部分函数式编程
- 控制函数执行,支持部分函数式编程
- 导入包import "github.com/duke-git/lancet/function"
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/function"
)
func main() {
var print = func(s string) {
fmt.Println(s)
}
function.Delay(2*time.Second, print, "hello world")
}
```
- Function list:
```go
func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value //创建一个函数, 只有在运行了n次之后才有效果
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value //创建一个函数,调用不超过n次。 当n已经达到时最后一个函数调用的结果将被记住并返回
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 Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool //每隔duration时间调用函数, 关闭返回通道可以停止调用
```
#### 7. netutil网络处理包
- 处理ip, http请求相关函数
- 导入包import "github.com/duke-git/lancet/netutil"
@@ -292,9 +324,10 @@ func HttpPut(url string, params ...interface{}) (*http.Response, error) //http p
func HttpDelete(url string, params ...interface{}) (*http.Response, error) //http delete请求
func HttpPatch(url string, params ...interface{}) (*http.Response, error) //http patch请求
func ConvertMapToQueryString(param map[string]interface{}) string //将map转换成url query string
func ParseHttpResponse(resp *http.Response, obj interface{}) error //将http响应解码成特定interface
```
#### 7. random随机数处理包
#### 8. random随机数处理包
- 生成和处理随机数
- 导入包import "github.com/duke-git/lancet/random"
@@ -323,7 +356,7 @@ func RandInt(min, max int) int //生成随机int
func RandString(length int) string //生成随机string
```
#### 8. slice切片操作包
#### 9. slice切片操作包
- 切片操作相关函数
- 导入包import "github.com/duke-git/lancet/slice"
@@ -359,6 +392,7 @@ func Find(slice, function interface{}) interface{} //查找slice中第一个符
func Filter(slice, function interface{}) interface{} //过滤slice, 函数签名func(index int, value interface{}) bool
func IntSlice(slice interface{}) ([]int, error) //转成int切片
func InterfaceSlice(slice interface{}) []interface{} //转成interface{}切片
func Intersection(slices ...interface{}) interface{} //slice交集去重
func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置插入value
func Map(slice, function interface{}) interface{} //遍历切片, 函数签名func(index int, value interface{}) interface{}
func ReverseSlice(slice interface{}) //反转切片
@@ -367,10 +401,12 @@ func Some(slice, function interface{}) bool //slice中任意一个元素都符
func SortByField(slice interface{}, field string, sortType ...string) error //对struct切片进行排序
func StringSlice(slice interface{}) []string //转为string切片
func Unique(slice interface{}) interface{} //去重切片
func Union(slices ...interface{}) interface{} //slice并集, 去重
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置更新value
func Without(slice interface{}, values ...interface{}) interface{} //slice去除values
```
#### 9. strutil字符串处理包
#### 10. strutil字符串处理包
- 字符串操作相关函数
- 导入包import "github.com/duke-git/lancet/strutil"
@@ -410,7 +446,7 @@ func ReverseStr(s string) string //字符串逆袭
func SnakeCase(s string) string //字符串转为SnakeCase, "fooBar" -> "foo_bar"
```
#### 10. validator验证器包
#### 11. validator验证器包
- 数据校验相关函数
- 导入包import "github.com/duke-git/lancet/validator"

View File

@@ -135,4 +135,4 @@ func TestReadFileByLine(t *testing.T) {
if !reflect.DeepEqual(res, expected) {
utils.LogFailedTestInfo(t, "ReadFileByLine", path, expected, res)
}
}
}

85
function/function.go Normal file
View File

@@ -0,0 +1,85 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package function implements some functions for control the function execution and some is for functional programming.
package function
import (
"reflect"
"time"
)
// After creates a function that invokes func once it's called n or more times
func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
return func(args ...interface{}) []reflect.Value {
n--
if n < 1 {
return invokeFunc(fn, args...)
}
return nil
}
}
// Before creates a function that invokes func once it's called less than n times
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
var res []reflect.Value
return func(args ...interface{}) []reflect.Value {
if n > 0 {
res = invokeFunc(fn, args...)
}
if n <= 0 {
fn = nil
}
n--
return res
}
}
// Fn is for curry function which is func(...interface{}) interface{}
type Fn func(...interface{}) interface{}
// Curry make a curry function
func (f Fn) Curry(i interface{}) func(...interface{}) interface{} {
return func(values ...interface{}) interface{} {
v := append([]interface{}{i}, values...)
return f(v...)
}
}
// Compose compose the functions from right to left
func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} {
return func(s ...interface{}) interface{} {
f := fnList[0]
restFn := fnList[1:]
if len(fnList) == 1 {
return f(s...)
}
return f(Compose(restFn...)(s...))
}
}
// Delay make the function execution after delayed time
func Delay(delay time.Duration, fn interface{}, args ...interface{}) {
time.Sleep(delay)
invokeFunc(fn, args...)
}
// Schedule invoke function every duration time, util close the returned bool chan
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool {
quit := make(chan bool)
go func() {
for {
invokeFunc(fn, args...)
select {
case <-time.After(d):
case <-quit:
return
}
}
}()
return quit
}

114
function/function_test.go Normal file
View File

@@ -0,0 +1,114 @@
package function
import (
"fmt"
"reflect"
"strings"
"testing"
"time"
)
func TestAfter(t *testing.T) {
arr := []string{"a", "b"}
f := After(len(arr), func(i int) int {
fmt.Println("print done")
return i
})
type cb func(args ...interface{}) []reflect.Value
print := func(i int, s string, fn cb) {
fmt.Printf("print: arr[%d] is %s \n", i, s)
v := fn(i)
if v != nil {
vv := v[0].Int()
if vv != 1 {
t.FailNow()
}
}
}
fmt.Println("print: arr is", arr)
for i := 0; i < len(arr); i++ {
print(i, arr[i], f)
}
}
func TestBefore(t *testing.T) {
arr := []string{"a", "b", "c", "d", "e"}
f := Before(3, func(i int) int {
return i
})
var res []int64
type cb func(args ...interface{}) []reflect.Value
appendStr := func(i int, s string, fn cb) {
fmt.Printf("appendStr: arr[%d] is %s \n", i, s)
v := fn(i)
res = append(res, v[0].Int())
}
for i := 0; i < len(arr); i++ {
appendStr(i, arr[i], f)
}
expect := []int64{0, 1, 2, 2, 2}
if !reflect.DeepEqual(expect, res) {
t.FailNow()
}
}
func TestCurry(t *testing.T) {
add := func(a, b int) int {
return a + b
}
var addCurry Fn = func(values ...interface{}) interface{} {
return add(values[0].(int), values[1].(int))
}
add1 := addCurry.Curry(1)
v := add1(2)
if v != 3 {
t.FailNow()
}
}
func TestCompose(t *testing.T) {
toUpper := func(a ...interface{}) interface{} {
return strings.ToUpper(a[0].(string))
}
toLower := func(a ...interface{}) interface{} {
return strings.ToLower(a[0].(string))
}
expect := toUpper(toLower("aBCde"))
cf := Compose(toUpper, toLower)
res := cf("aBCde")
if res != expect {
t.FailNow()
}
}
func TestDelay(t *testing.T) {
var print = func(s string) {
fmt.Println(s)
}
Delay(2*time.Second, print, "test delay")
}
func TestSchedule(t *testing.T) {
var res []string
appendStr := func(s string) {
fmt.Println(s)
res = append(res, s)
}
stop := Schedule(1*time.Second, appendStr, "*")
time.Sleep(5 * time.Second)
close(stop)
expect := []string{"*", "*", "*", "*", "*"}
if !reflect.DeepEqual(expect, res) {
t.FailNow()
}
fmt.Println("done")
}

23
function/function_util.go Normal file
View File

@@ -0,0 +1,23 @@
package function
import (
"fmt"
"reflect"
)
func invokeFunc(fn interface{}, args ...interface{}) []reflect.Value {
fv := functionValue(fn)
params := make([]reflect.Value, len(args))
for i, item := range args {
params[i] = reflect.ValueOf(item)
}
return fv.Call(params)
}
func functionValue(function interface{}) reflect.Value {
v := reflect.ValueOf(function)
if v.Kind() != reflect.Func {
panic(fmt.Sprintf("Invalid function type, value of type %T", function))
}
return v
}

View File

@@ -12,6 +12,8 @@
package netutil
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"sort"
@@ -43,6 +45,15 @@ func HttpPatch(url string, params ...interface{}) (*http.Response, error) {
return request(http.MethodPatch, url, params...)
}
// ParseHttpResponse decode http response to specified interface
func ParseHttpResponse(resp *http.Response, obj interface{}) error {
if resp == nil {
return errors.New("InvalidResp")
}
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(obj)
}
// ConvertMapToQueryString convert map to sorted url query string
func ConvertMapToQueryString(param map[string]interface{}) string {
if param == nil {

View File

@@ -102,3 +102,27 @@ func TestConvertMapToQueryString(t *testing.T) {
t.FailNow()
}
}
func TestParseResponse(t *testing.T) {
url := "http://public-api-v1.aspirantzhang.com/users"
type User struct {
Id int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
type UserResp struct {
Data []User `json:"data"`
}
resp, err := HttpGet(url)
if err != nil {
log.Fatal(err)
t.FailNow()
}
userResp := &UserResp{}
err = ParseHttpResponse(resp, userResp)
if err != nil {
log.Fatal(err)
t.FailNow()
}
fmt.Println(userResp.Data)
}

View File

@@ -97,7 +97,7 @@ func Every(slice, function interface{}) bool {
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
panic("Filter function must be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
}
var indexes []int
@@ -119,18 +119,18 @@ func Some(slice, function interface{}) bool {
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
panic("Filter function must be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
}
var indexes []int
has := false
for i := 0; i < sv.Len(); i++ {
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
if flag.Bool() {
indexes = append(indexes, i)
has = true
}
}
return len(indexes) > 0
return has
}
// Filter iterates over elements of slice, returning an slice of all elements `signature` returns truthy for.
@@ -141,7 +141,7 @@ func Filter(slice, function interface{}) interface{} {
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
panic("Filter function must be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
}
var indexes []int
@@ -167,7 +167,7 @@ func Find(slice, function interface{}) interface{} {
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
panic("Filter function must be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
}
var index int
@@ -189,7 +189,7 @@ func Map(slice, function interface{}) interface{} {
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, nil) {
panic("Map function must be of type func(int, " + elemType.String() + ")" + elemType.String())
panic("function param should be of type func(int, " + elemType.String() + ")" + elemType.String())
}
res := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len())
@@ -215,7 +215,7 @@ func Reduce(slice, function, zero interface{}) interface{} {
fn := functionValue(function)
if checkSliceCallbackFuncSignature(fn, elementType, elementType, elementType) {
t := elementType.String()
panic("Reduce function must be of type func(int, " + t + ", " + t + ")" + t)
panic("function param should be of type func(int, " + t + ", " + t + ")" + t)
}
var params [3]reflect.Value
@@ -379,22 +379,28 @@ func Unique(slice interface{}) interface{} {
return slice
}
var res []interface{}
var temp []interface{}
len := 0
for i := 0; i < sv.Len(); i++ {
v := sv.Index(i).Interface()
flag := true
for j := range res {
if v == res[j] {
flag = false
skip := true
for j := range temp {
if v == temp[j] {
skip = false
break
}
}
if flag {
res = append(res, v)
if skip {
temp = append(temp, v)
len++
}
}
return res
res := reflect.MakeSlice(sv.Type(), len, len)
for i := 0; i < len; i++ {
res.Index(i).Set(reflect.ValueOf(temp[i]))
}
return res.Interface()
// if use map filter, the result slice element order is random, not same as origin slice
//mp := make(map[interface{}]bool)
@@ -411,6 +417,66 @@ func Unique(slice interface{}) interface{} {
}
// Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons.
func Union(slices ...interface{}) interface{} {
if len(slices) == 0 {
return nil
}
// append all slices, then unique it
var allSlice []interface{}
len := 0
for i := range slices {
sv := sliceValue(slices[i])
len += sv.Len()
for j := 0; j < sv.Len(); j++ {
v := sv.Index(j).Interface()
allSlice = append(allSlice, v)
}
}
sv := sliceValue(slices[0])
res := reflect.MakeSlice(sv.Type(), len, len)
for i := 0; i < len; i++ {
res.Index(i).Set(reflect.ValueOf(allSlice[i]))
}
return Unique(res.Interface())
}
// Intersection creates a slice of unique values that included by all slices.
func Intersection(slices ...interface{}) interface{} {
if len(slices) == 0 {
return nil
}
reduceFunc := func(index int, slice1, slice2 interface{}) interface{} {
set := make([]interface{}, 0)
hash := make(map[interface{}]bool)
sv1 := reflect.ValueOf(slice1)
for i := 0; i < sv1.Len(); i++ {
v := sv1.Index(i).Interface()
hash[v] = true
}
sv2 := reflect.ValueOf(slice2)
for i := 0; i < sv2.Len(); i++ {
el := sv2.Index(i).Interface()
if _, found := hash[el]; found {
set = append(set, el)
}
}
res := reflect.MakeSlice(sv1.Type(), len(set), len(set))
for i := 0; i < len(set); i++ {
res.Index(i).Set(reflect.ValueOf(set[i]))
}
return res.Interface()
}
res := Reduce(slices, reduceFunc, nil)
return Union(res)
}
// ReverseSlice return slice of element order is reversed to the given slice
func ReverseSlice(slice interface{}) {
v := sliceValue(slice)
@@ -474,3 +540,25 @@ func SortByField(slice interface{}, field string, sortType ...string) error {
}
return nil
}
// Without creates a slice excluding all given values
func Without(slice interface{}, values ...interface{}) interface{} {
sv := sliceValue(slice)
if sv.Len() == 0 {
return slice
}
var indexes []int
for i := 0; i < sv.Len(); i++ {
v := sv.Index(i).Interface()
if !Contain(values, v) {
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()
}

View File

@@ -395,7 +395,7 @@ func updateByIndex(t *testing.T, test interface{}, index int, value, expected in
func TestUnique(t *testing.T) {
t1 := []int{1, 2, 2, 3}
e1 := []int{1, 2, 3}
r1, _ := IntSlice(Unique(t1))
r1 := Unique(t1)
if !reflect.DeepEqual(r1, e1) {
utils.LogFailedTestInfo(t, "Unique", t1, e1, r1)
t.FailNow()
@@ -403,13 +403,60 @@ func TestUnique(t *testing.T) {
t2 := []string{"a", "a", "b", "c"}
e2 := []string{"a", "b", "c"}
r2 := StringSlice(Unique(t2))
r2 := Unique(t2)
if !reflect.DeepEqual(r2, e2) {
utils.LogFailedTestInfo(t, "Unique", t2, e2, r2)
t.FailNow()
}
}
func TestUnion(t *testing.T) {
s1 := []int{1, 3, 4, 6}
s2 := []int{1, 2, 5, 6}
s3 := []int{0, 4, 5, 7}
expected1 := []int{1, 3, 4, 6, 2, 5, 0, 7}
res1 := Union(s1, s2, s3)
if !reflect.DeepEqual(res1, expected1) {
utils.LogFailedTestInfo(t, "Union", s1, expected1, res1)
t.FailNow()
}
expected2 := []int{1, 3, 4, 6}
res2 := Union(s1)
if !reflect.DeepEqual(res2, expected2) {
utils.LogFailedTestInfo(t, "Union", s1, expected2, res2)
t.FailNow()
}
}
func TestIntersection(t *testing.T) {
s1 := []int{1, 2, 2, 3}
s2 := []int{1, 2, 3, 4}
s3 := []int{0, 2, 3, 5, 6}
s4 := []int{0, 5, 6}
expected := [][]int{
{2, 3},
{1, 2, 3},
{1, 2, 3},
{},
}
res := []interface{}{
Intersection(s1, s2, s3),
Intersection(s1, s2),
Intersection(s1),
Intersection(s1, s4),
}
for i := 0; i < len(res); i++ {
if !reflect.DeepEqual(res[i], expected[i]) {
utils.LogFailedTestInfo(t, "Intersection", "Intersection", expected[i], res[i])
t.FailNow()
}
}
}
func TestReverseSlice(t *testing.T) {
s1 := []int{1, 2, 3, 4, 5}
e1 := []int{5, 4, 3, 2, 1}
@@ -469,3 +516,14 @@ func TestSortByField(t *testing.T) {
}
}
func TestWithout(t *testing.T) {
s := []int{1, 2, 3, 4, 5}
expected := []int{3, 4, 5}
res := Without(s, 1, 2)
if !reflect.DeepEqual(res, expected) {
utils.LogFailedTestInfo(t, "Without", s, expected, res)
t.FailNow()
}
}

View File

@@ -7,6 +7,7 @@ package strutil
import (
"regexp"
"strings"
"unicode"
)
// CamelCase covert string to camelCase string.
@@ -40,18 +41,16 @@ func Capitalize(s string) string {
return ""
}
res := ""
for i, v := range []rune(s) {
out := make([]rune, len(s))
for i, v := range s {
if i == 0 {
if v >= 97 && v <= 122 {
v -= 32
}
res += string(v)
out[i] = unicode.ToUpper(v)
} else {
res += strings.ToLower(string(v))
out[i] = unicode.ToLower(v)
}
}
return res
return string(out)
}
// LowerFirst converts the first character of string to lower case.

View File

@@ -11,11 +11,11 @@ import (
"unicode"
)
var isAlphaRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
// IsAlpha checks if the string contains only letters (a-zA-Z)
func IsAlpha(s string) bool {
pattern := `^[a-zA-Z]+$`
reg := regexp.MustCompile(pattern)
return reg.MatchString(s)
return isAlphaRegexMatcher.MatchString(s)
}
// IsNumberStr check if the string can convert to a number.
@@ -29,10 +29,11 @@ func IsFloatStr(s string) bool {
return e == nil
}
var isIntStrRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
// IsIntStr check if the string can convert to a integer.
func IsIntStr(s string) bool {
match, _ := regexp.MatchString(`^[\+-]?\d+$`, s)
return match
return isIntStrRegexMatcher.MatchString(s)
}
// IsIp check if the string is a ip address.
@@ -71,61 +72,61 @@ func IsIpV6(ipstr string) bool {
return false
}
var isDnsRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`)
// IsDns check if the string is dns.
func IsDns(dns string) bool {
pattern := `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`
reg := regexp.MustCompile(pattern)
return reg.MatchString(dns)
return isDnsRegexMatcher.MatchString(dns)
}
var isEmailRegexMatcher *regexp.Regexp = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`)
// IsEmail check if the string is a email address.
func IsEmail(email string) bool {
pattern := `\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`
reg := regexp.MustCompile(pattern)
return reg.MatchString(email)
return isEmailRegexMatcher.MatchString(email)
}
var isChineseMobileRegexMatcher *regexp.Regexp = regexp.MustCompile("^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$")
// IsChineseMobile check if the string is chinese mobile number.
func IsChineseMobile(mobileNum string) bool {
pattern := "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$"
reg := regexp.MustCompile(pattern)
return reg.MatchString(mobileNum)
return isChineseMobileRegexMatcher.MatchString(mobileNum)
}
var isChineseIdNumRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[1-9]\d{5}(18|19|20|21|22)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`)
// IsChineseIdNum check if the string is chinese id number.
func IsChineseIdNum(id string) bool {
pattern := `^[1-9]\d{5}(18|19|20|21|22)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`
reg := regexp.MustCompile(pattern)
return reg.MatchString(id)
return isChineseIdNumRegexMatcher.MatchString(id)
}
var containChineseRegexMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
// ContainChinese check if the string contain mandarin chinese.
func ContainChinese(s string) bool {
pattern := "[\u4e00-\u9fa5]"
reg := regexp.MustCompile(pattern)
return reg.MatchString(s)
return containChineseRegexMatcher.MatchString(s)
}
var isChinesePhoneRegexMatcher *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}`)
// IsChinesePhone check if the string is chinese phone number.
// Valid chinese phone is xxx-xxxxxxxx or xxxx-xxxxxxx
func IsChinesePhone(phone string) bool {
pattern := `\d{3}-\d{8}|\d{4}-\d{7}`
reg := regexp.MustCompile(pattern)
return reg.MatchString(phone)
return isChinesePhoneRegexMatcher.MatchString(phone)
}
var isCreditCardRegexMatcher *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`)
// IsCreditCard check if the string is credit card.
func IsCreditCard(creditCart string) bool {
pattern := `^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`
reg := regexp.MustCompile(pattern)
return reg.MatchString(creditCart)
return isCreditCardRegexMatcher.MatchString(creditCart)
}
var isBase64RegexMatcher *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`)
// IsBase64 check if the string is base64 string.
func IsBase64(base64 string) bool {
pattern := `^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`
reg := regexp.MustCompile(pattern)
return reg.MatchString(base64)
return isBase64RegexMatcher.MatchString(base64)
}
// IsEmptyString check if the string is empty.