mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-08 14:42:27 +08:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
051f20caef | ||
|
|
613785b07c | ||
|
|
0b0eb695e8 | ||
|
|
745082fff1 | ||
|
|
24b8da360e | ||
|
|
b106c428ae | ||
|
|
8b1171d0cb | ||
|
|
ab012f2545 | ||
|
|
a952cb208a | ||
|
|
f239a8ca8e | ||
|
|
5c6626b37e | ||
|
|
16b5101600 | ||
|
|
ec983b7aa6 | ||
|
|
56fc2aabd6 | ||
|
|
0ddd52225b | ||
|
|
3919160e38 | ||
|
|
40ec5bc0f6 | ||
|
|
99faeccb05 | ||
|
|
ad777bc877 | ||
|
|
589ce7404f | ||
|
|
6ab4fca433 | ||
|
|
b33a9cbfe3 | ||
|
|
9ad9d1805b | ||
|
|
0a1386f5a7 | ||
|
|
b5541ea177 | ||
|
|
578b1bba65 | ||
|
|
3045d56503 | ||
|
|
f1dbd943aa | ||
|
|
e87f3b70f0 | ||
|
|
26b59dd56b | ||
|
|
143aba7112 | ||
|
|
60f3a72c88 | ||
|
|
d1b74cfcfb | ||
|
|
72a89be8c1 |
2
.github/workflows/codecov.yml
vendored
2
.github/workflows/codecov.yml
vendored
@@ -17,6 +17,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: "1.16"
|
go-version: "1.16"
|
||||||
- name: Run coverage
|
- 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
|
- name: Upload coverage to Codecov
|
||||||
run: bash <(curl -s https://codecov.io/bash)
|
run: bash <(curl -s https://codecov.io/bash)
|
||||||
|
|||||||
48
README.md
48
README.md
@@ -6,7 +6,7 @@
|
|||||||
<div align="center" style="text-align: center;">
|
<div align="center" style="text-align: center;">
|
||||||
|
|
||||||

|

|
||||||
[](https://github.com/duke-git/lancet/releases)
|
[](https://github.com/duke-git/lancet/releases)
|
||||||
[](https://pkg.go.dev/github.com/duke-git/lancet)
|
[](https://pkg.go.dev/github.com/duke-git/lancet)
|
||||||
[](https://goreportcard.com/report/github.com/duke-git/lancet)
|
[](https://goreportcard.com/report/github.com/duke-git/lancet)
|
||||||
[](https://codecov.io/gh/duke-git/lancet)
|
[](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
|
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.
|
- Ip and http request method.
|
||||||
- Usage: import "github.com/duke-git/lancet/netutil".
|
- 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 HttpDelete(url string, params ...interface{}) (*http.Response, error) //http delete request
|
||||||
func HttpPatch(url string, params ...interface{}) (*http.Response, error) //http patch 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 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.
|
- Generate random string and int.
|
||||||
- Usage: import "github.com/duke-git/lancet/random".
|
- 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
|
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.
|
- Contain function for process slice.
|
||||||
- Usage: import "github.com/duke-git/lancet/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 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 IntSlice(slice interface{}) ([]int, error) //convert value to int slice
|
||||||
func InterfaceSlice(slice interface{}) []interface{} //convert value to interface{} 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 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 Map(slice, function interface{}) interface{} //map lisce, function signature should be func(index int, value interface{}) interface{}
|
||||||
func ReverseSlice(slice interface{}) //revere slice
|
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 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 StringSlice(slice interface{}) []string //convert value to string slice
|
||||||
func Unique(slice interface{}) interface{} //remove duplicate elements in 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 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
|
- Contain functions to precess string
|
||||||
- Usage: import "github.com/duke-git/lancet/strutil"
|
- 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"
|
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.
|
- Contain function for data validation.
|
||||||
- Usage: import "github.com/duke-git/lancet/validator".
|
- Usage: import "github.com/duke-git/lancet/validator".
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div align="center" style="text-align: center;">
|
<div align="center" style="text-align: center;">
|
||||||
|
|
||||||

|

|
||||||
[](https://github.com/duke-git/lancet/releases)
|
[](https://github.com/duke-git/lancet/releases)
|
||||||
[](https://pkg.go.dev/github.com/duke-git/lancet)
|
[](https://pkg.go.dev/github.com/duke-git/lancet)
|
||||||
[](https://goreportcard.com/report/github.com/duke-git/lancet)
|
[](https://goreportcard.com/report/github.com/duke-git/lancet)
|
||||||
[](https://codecov.io/gh/duke-git/lancet)
|
[](https://codecov.io/gh/duke-git/lancet)
|
||||||
@@ -247,7 +247,39 @@ func main() {
|
|||||||
func Comma(v interface{}, symbol string) string //用逗号每隔3位分割数字/字符串
|
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请求相关函数
|
- 处理ip, http请求相关函数
|
||||||
- 导入包:import "github.com/duke-git/lancet/netutil"
|
- 导入包: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 HttpDelete(url string, params ...interface{}) (*http.Response, error) //http delete请求
|
||||||
func HttpPatch(url string, params ...interface{}) (*http.Response, error) //http patch请求
|
func HttpPatch(url string, params ...interface{}) (*http.Response, error) //http patch请求
|
||||||
func ConvertMapToQueryString(param map[string]interface{}) string //将map转换成url query string
|
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"
|
- 导入包:import "github.com/duke-git/lancet/random"
|
||||||
@@ -323,7 +356,7 @@ func RandInt(min, max int) int //生成随机int
|
|||||||
func RandString(length int) string //生成随机string
|
func RandString(length int) string //生成随机string
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 8. slice切片操作包
|
#### 9. slice切片操作包
|
||||||
|
|
||||||
- 切片操作相关函数
|
- 切片操作相关函数
|
||||||
- 导入包:import "github.com/duke-git/lancet/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 Filter(slice, function interface{}) interface{} //过滤slice, 函数签名:func(index int, value interface{}) bool
|
||||||
func IntSlice(slice interface{}) ([]int, error) //转成int切片
|
func IntSlice(slice interface{}) ([]int, error) //转成int切片
|
||||||
func InterfaceSlice(slice interface{}) []interface{} //转成interface{}切片
|
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 InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置插入value
|
||||||
func Map(slice, function interface{}) interface{} //遍历切片, 函数签名:func(index int, value interface{}) interface{}
|
func Map(slice, function interface{}) interface{} //遍历切片, 函数签名:func(index int, value interface{}) interface{}
|
||||||
func ReverseSlice(slice 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 SortByField(slice interface{}, field string, sortType ...string) error //对struct切片进行排序
|
||||||
func StringSlice(slice interface{}) []string //转为string切片
|
func StringSlice(slice interface{}) []string //转为string切片
|
||||||
func Unique(slice interface{}) interface{} //去重切片
|
func Unique(slice interface{}) interface{} //去重切片
|
||||||
|
func Union(slices ...interface{}) interface{} //slice并集, 去重
|
||||||
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置更新value
|
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"
|
- 导入包: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"
|
func SnakeCase(s string) string //字符串转为SnakeCase, "fooBar" -> "foo_bar"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 10. validator验证器包
|
#### 11. validator验证器包
|
||||||
|
|
||||||
- 数据校验相关函数
|
- 数据校验相关函数
|
||||||
- 导入包:import "github.com/duke-git/lancet/validator"
|
- 导入包:import "github.com/duke-git/lancet/validator"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
package datetime
|
package datetime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,24 +56,17 @@ func init() {
|
|||||||
|
|
||||||
// AddMinute add or sub minute to the time
|
// AddMinute add or sub minute to the time
|
||||||
func AddMinute(t time.Time, minute int64) time.Time {
|
func AddMinute(t time.Time, minute int64) time.Time {
|
||||||
s := strconv.FormatInt(minute, 10)
|
return t.Add(time.Minute * time.Duration(minute))
|
||||||
m, _ := time.ParseDuration(s + "m")
|
|
||||||
return t.Add(m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddHour add or sub hour to the time
|
// AddHour add or sub hour to the time
|
||||||
func AddHour(t time.Time, hour int64) time.Time {
|
func AddHour(t time.Time, hour int64) time.Time {
|
||||||
s := strconv.FormatInt(hour, 10)
|
return t.Add(time.Hour * time.Duration(hour))
|
||||||
h, _ := time.ParseDuration(s + "h")
|
|
||||||
return t.Add(h)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddDay add or sub day to the time
|
// AddDay add or sub day to the time
|
||||||
func AddDay(t time.Time, day int64) time.Time {
|
func AddDay(t time.Time, day int64) time.Time {
|
||||||
dayHours := day * 24
|
return t.Add(24 * time.Hour * time.Duration(day))
|
||||||
d := strconv.FormatInt(dayHours, 10)
|
|
||||||
h, _ := time.ParseDuration(d + "h")
|
|
||||||
return t.Add(h)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNowDate return format yyyy-mm-dd of current date
|
// GetNowDate return format yyyy-mm-dd of current date
|
||||||
@@ -109,7 +102,11 @@ func FormatTimeToStr(t time.Time, format string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FormatStrToTime convert string to time
|
// FormatStrToTime convert string to time
|
||||||
func FormatStrToTime(str, format string) time.Time {
|
func FormatStrToTime(str, format string) (time.Time, error) {
|
||||||
t, _ := time.Parse(timeFormat[format], str)
|
v, ok := timeFormat[format]
|
||||||
return t
|
if !ok {
|
||||||
|
return time.Time{}, fmt.Errorf("format %s not found", format)
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Parse(v, str)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,10 @@ func TestFormatStrToTime(t *testing.T) {
|
|||||||
"2021/01"}
|
"2021/01"}
|
||||||
|
|
||||||
for i := 0; i < len(cases); i++ {
|
for i := 0; i < len(cases); i++ {
|
||||||
res := FormatStrToTime(datetimeStr[i], cases[i])
|
res, err := FormatStrToTime(datetimeStr[i], cases[i])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
expected, _ := time.Parse(formats[i], datetimeStr[i])
|
expected, _ := time.Parse(formats[i], datetimeStr[i])
|
||||||
if res != expected {
|
if res != expected {
|
||||||
utils.LogFailedTestInfo(t, "FormatTimeToStr", cases[i], expected, res)
|
utils.LogFailedTestInfo(t, "FormatTimeToStr", cases[i], expected, res)
|
||||||
|
|||||||
90
function/function.go
Normal file
90
function/function.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// 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 {
|
||||||
|
// Catch programming error while constructing the closure
|
||||||
|
MustBeFunction(fn)
|
||||||
|
return func(args ...interface{}) []reflect.Value {
|
||||||
|
n--
|
||||||
|
if n < 1 {
|
||||||
|
return unsafeInvokeFunc(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 {
|
||||||
|
// Catch programming error while constructing the closure
|
||||||
|
MustBeFunction(fn)
|
||||||
|
var res []reflect.Value
|
||||||
|
return func(args ...interface{}) []reflect.Value {
|
||||||
|
if n > 0 {
|
||||||
|
res = unsafeInvokeFunc(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 {
|
||||||
|
// Catch programming error while constructing the closure
|
||||||
|
MustBeFunction(fn)
|
||||||
|
quit := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
unsafeInvokeFunc(fn, args...)
|
||||||
|
select {
|
||||||
|
case <-time.After(d):
|
||||||
|
case <-quit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return quit
|
||||||
|
}
|
||||||
114
function/function_test.go
Normal file
114
function/function_test.go
Normal 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")
|
||||||
|
}
|
||||||
39
function/function_util.go
Normal file
39
function/function_util.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
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 unsafeInvokeFunc(fn interface{}, args ...interface{}) []reflect.Value {
|
||||||
|
fv := reflect.ValueOf(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
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustBeFunction(function interface{}) {
|
||||||
|
v := reflect.ValueOf(function)
|
||||||
|
if v.Kind() != reflect.Func {
|
||||||
|
panic(fmt.Sprintf("Invalid function type, value of type %T", function))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,8 @@
|
|||||||
package netutil
|
package netutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -43,6 +45,15 @@ func HttpPatch(url string, params ...interface{}) (*http.Response, error) {
|
|||||||
return request(http.MethodPatch, url, params...)
|
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
|
// ConvertMapToQueryString convert map to sorted url query string
|
||||||
func ConvertMapToQueryString(param map[string]interface{}) string {
|
func ConvertMapToQueryString(param map[string]interface{}) string {
|
||||||
if param == nil {
|
if param == nil {
|
||||||
|
|||||||
@@ -11,16 +11,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestHttpGet(t *testing.T) {
|
func TestHttpGet(t *testing.T) {
|
||||||
_, e := HttpGet("", nil)
|
url := "https://jsonplaceholder.typicode.com/todos/1"
|
||||||
if e == nil {
|
header := map[string]string{
|
||||||
t.FailNow()
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
url := "https://gutendex.com/books?"
|
resp, err := HttpGet(url, header)
|
||||||
queryParams := make(map[string]interface{})
|
|
||||||
queryParams["ids"] = "1"
|
|
||||||
|
|
||||||
resp, err := HttpGet(url, nil, queryParams)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
@@ -28,23 +24,20 @@ func TestHttpGet(t *testing.T) {
|
|||||||
|
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHttpPost(t *testing.T) {
|
func TestHttpPost(t *testing.T) {
|
||||||
url := "http://public-api-v1.aspirantzhang.com/users"
|
url := "https://jsonplaceholder.typicode.com/todos"
|
||||||
type User struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
}
|
|
||||||
user := User{
|
|
||||||
"test",
|
|
||||||
"test@test.com",
|
|
||||||
}
|
|
||||||
bodyParams, _ := json.Marshal(user)
|
|
||||||
header := map[string]string{
|
header := map[string]string{
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
type Todo struct {
|
||||||
|
UserId int `json:"userId"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
todo := Todo{1, "TestAddToDo"}
|
||||||
|
bodyParams, _ := json.Marshal(todo)
|
||||||
|
|
||||||
resp, err := HttpPost(url, header, nil, bodyParams)
|
resp, err := HttpPost(url, header, nil, bodyParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -55,19 +48,18 @@ func TestHttpPost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHttpPut(t *testing.T) {
|
func TestHttpPut(t *testing.T) {
|
||||||
url := "http://public-api-v1.aspirantzhang.com/users/10420"
|
url := "https://jsonplaceholder.typicode.com/todos/1"
|
||||||
type User struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
}
|
|
||||||
user := User{
|
|
||||||
"test",
|
|
||||||
"test@test.com",
|
|
||||||
}
|
|
||||||
bodyParams, _ := json.Marshal(user)
|
|
||||||
header := map[string]string{
|
header := map[string]string{
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
type Todo struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
UserId int `json:"userId"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
todo := Todo{1, 1, "TestPutToDo"}
|
||||||
|
bodyParams, _ := json.Marshal(todo)
|
||||||
|
|
||||||
resp, err := HttpPut(url, header, nil, bodyParams)
|
resp, err := HttpPut(url, header, nil, bodyParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -77,8 +69,30 @@ func TestHttpPut(t *testing.T) {
|
|||||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHttpPatch(t *testing.T) {
|
||||||
|
url := "https://jsonplaceholder.typicode.com/todos/1"
|
||||||
|
header := map[string]string{
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
type Todo struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
UserId int `json:"userId"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
todo := Todo{1, 1, "TestPatchToDo"}
|
||||||
|
bodyParams, _ := json.Marshal(todo)
|
||||||
|
|
||||||
|
resp, err := HttpPatch(url, header, nil, bodyParams)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
func TestHttpDelete(t *testing.T) {
|
func TestHttpDelete(t *testing.T) {
|
||||||
url := "http://public-api-v1.aspirantzhang.com/users/10420"
|
url := "https://jsonplaceholder.typicode.com/todos/1"
|
||||||
resp, err := HttpDelete(url)
|
resp, err := HttpDelete(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -102,3 +116,31 @@ func TestConvertMapToQueryString(t *testing.T) {
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseResponse(t *testing.T) {
|
||||||
|
url := "https://jsonplaceholder.typicode.com/todos/1"
|
||||||
|
header := map[string]string{
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := HttpGet(url, header)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Todo struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
UserId int `json:"userId"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Completed bool `json:"completed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
toDoResp := &Todo{}
|
||||||
|
err = ParseHttpResponse(resp, toDoResp)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
fmt.Println("response: ", toDoResp)
|
||||||
|
}
|
||||||
|
|||||||
128
slice/slice.go
128
slice/slice.go
@@ -97,18 +97,18 @@ func Every(slice, function interface{}) bool {
|
|||||||
|
|
||||||
elemType := sv.Type().Elem()
|
elemType := sv.Type().Elem()
|
||||||
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
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
|
var currentLength int
|
||||||
for i := 0; i < sv.Len(); i++ {
|
for i := 0; i < sv.Len(); i++ {
|
||||||
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
|
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
|
||||||
if flag.Bool() {
|
if flag.Bool() {
|
||||||
indexes = append(indexes, i)
|
currentLength++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return len(indexes) == sv.Len()
|
return currentLength == sv.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some return true if any of the values in the list pass the predicate function.
|
// Some return true if any of the values in the list pass the predicate function.
|
||||||
@@ -119,18 +119,18 @@ func Some(slice, function interface{}) bool {
|
|||||||
|
|
||||||
elemType := sv.Type().Elem()
|
elemType := sv.Type().Elem()
|
||||||
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
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++ {
|
for i := 0; i < sv.Len(); i++ {
|
||||||
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
|
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
|
||||||
if flag.Bool() {
|
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.
|
// 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()
|
elemType := sv.Type().Elem()
|
||||||
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
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
|
var indexes []int
|
||||||
@@ -167,7 +167,7 @@ func Find(slice, function interface{}) interface{} {
|
|||||||
|
|
||||||
elemType := sv.Type().Elem()
|
elemType := sv.Type().Elem()
|
||||||
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
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
|
var index int
|
||||||
@@ -189,7 +189,7 @@ func Map(slice, function interface{}) interface{} {
|
|||||||
|
|
||||||
elemType := sv.Type().Elem()
|
elemType := sv.Type().Elem()
|
||||||
if checkSliceCallbackFuncSignature(fn, elemType, nil) {
|
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())
|
res := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len())
|
||||||
@@ -215,7 +215,7 @@ func Reduce(slice, function, zero interface{}) interface{} {
|
|||||||
fn := functionValue(function)
|
fn := functionValue(function)
|
||||||
if checkSliceCallbackFuncSignature(fn, elementType, elementType, elementType) {
|
if checkSliceCallbackFuncSignature(fn, elementType, elementType, elementType) {
|
||||||
t := elementType.String()
|
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
|
var params [3]reflect.Value
|
||||||
@@ -379,22 +379,28 @@ func Unique(slice interface{}) interface{} {
|
|||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
var res []interface{}
|
var temp []interface{}
|
||||||
|
len := 0
|
||||||
for i := 0; i < sv.Len(); i++ {
|
for i := 0; i < sv.Len(); i++ {
|
||||||
v := sv.Index(i).Interface()
|
v := sv.Index(i).Interface()
|
||||||
flag := true
|
skip := true
|
||||||
for j := range res {
|
for j := range temp {
|
||||||
if v == res[j] {
|
if v == temp[j] {
|
||||||
flag = false
|
skip = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if flag {
|
if skip {
|
||||||
res = append(res, v)
|
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
|
// if use map filter, the result slice element order is random, not same as origin slice
|
||||||
//mp := make(map[interface{}]bool)
|
//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 Unique(res)
|
||||||
|
}
|
||||||
|
|
||||||
// ReverseSlice return slice of element order is reversed to the given slice
|
// ReverseSlice return slice of element order is reversed to the given slice
|
||||||
func ReverseSlice(slice interface{}) {
|
func ReverseSlice(slice interface{}) {
|
||||||
v := sliceValue(slice)
|
v := sliceValue(slice)
|
||||||
@@ -474,3 +540,25 @@ func SortByField(slice interface{}, field string, sortType ...string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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()
|
||||||
|
}
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ func updateByIndex(t *testing.T, test interface{}, index int, value, expected in
|
|||||||
func TestUnique(t *testing.T) {
|
func TestUnique(t *testing.T) {
|
||||||
t1 := []int{1, 2, 2, 3}
|
t1 := []int{1, 2, 2, 3}
|
||||||
e1 := []int{1, 2, 3}
|
e1 := []int{1, 2, 3}
|
||||||
r1, _ := IntSlice(Unique(t1))
|
r1 := Unique(t1)
|
||||||
if !reflect.DeepEqual(r1, e1) {
|
if !reflect.DeepEqual(r1, e1) {
|
||||||
utils.LogFailedTestInfo(t, "Unique", t1, e1, r1)
|
utils.LogFailedTestInfo(t, "Unique", t1, e1, r1)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
@@ -403,13 +403,60 @@ func TestUnique(t *testing.T) {
|
|||||||
|
|
||||||
t2 := []string{"a", "a", "b", "c"}
|
t2 := []string{"a", "a", "b", "c"}
|
||||||
e2 := []string{"a", "b", "c"}
|
e2 := []string{"a", "b", "c"}
|
||||||
r2 := StringSlice(Unique(t2))
|
r2 := Unique(t2)
|
||||||
if !reflect.DeepEqual(r2, e2) {
|
if !reflect.DeepEqual(r2, e2) {
|
||||||
utils.LogFailedTestInfo(t, "Unique", t2, e2, r2)
|
utils.LogFailedTestInfo(t, "Unique", t2, e2, r2)
|
||||||
t.FailNow()
|
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) {
|
func TestReverseSlice(t *testing.T) {
|
||||||
s1 := []int{1, 2, 3, 4, 5}
|
s1 := []int{1, 2, 3, 4, 5}
|
||||||
e1 := []int{5, 4, 3, 2, 1}
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ package strutil
|
|||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CamelCase covert string to camelCase string.
|
// CamelCase covert string to camelCase string.
|
||||||
@@ -40,18 +42,16 @@ func Capitalize(s string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
res := ""
|
out := make([]rune, len(s))
|
||||||
for i, v := range []rune(s) {
|
for i, v := range s {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
if v >= 97 && v <= 122 {
|
out[i] = unicode.ToUpper(v)
|
||||||
v -= 32
|
|
||||||
}
|
|
||||||
res += string(v)
|
|
||||||
} else {
|
} 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.
|
// LowerFirst converts the first character of string to lower case.
|
||||||
@@ -60,20 +60,10 @@ func LowerFirst(s string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
res := ""
|
r, size := utf8.DecodeRuneInString(s)
|
||||||
for i, v := range []rune(s) {
|
r = unicode.ToLower(r)
|
||||||
if i == 0 {
|
|
||||||
if v >= 65 && v <= 96 {
|
return string(r) + s[size:]
|
||||||
v += 32
|
|
||||||
res += string(v)
|
|
||||||
} else {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res += string(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PadEnd pads string on the right side if it's shorter than size.
|
// PadEnd pads string on the right side if it's shorter than size.
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ func TestLowerFirst(t *testing.T) {
|
|||||||
lowerFirst(t, "foo", "foo")
|
lowerFirst(t, "foo", "foo")
|
||||||
lowerFirst(t, "BAR", "bAR")
|
lowerFirst(t, "BAR", "bAR")
|
||||||
lowerFirst(t, "FOo", "fOo")
|
lowerFirst(t, "FOo", "fOo")
|
||||||
|
lowerFirst(t, "FOo大", "fOo大")
|
||||||
}
|
}
|
||||||
|
|
||||||
func lowerFirst(t *testing.T, test string, expected string) {
|
func lowerFirst(t *testing.T, test string, expected string) {
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import (
|
|||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var isAlphaRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
|
||||||
|
|
||||||
// IsAlpha checks if the string contains only letters (a-zA-Z)
|
// IsAlpha checks if the string contains only letters (a-zA-Z)
|
||||||
func IsAlpha(s string) bool {
|
func IsAlpha(s string) bool {
|
||||||
pattern := `^[a-zA-Z]+$`
|
return isAlphaRegexMatcher.MatchString(s)
|
||||||
reg := regexp.MustCompile(pattern)
|
|
||||||
return reg.MatchString(s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNumberStr check if the string can convert to a number.
|
// IsNumberStr check if the string can convert to a number.
|
||||||
@@ -29,10 +29,11 @@ func IsFloatStr(s string) bool {
|
|||||||
return e == nil
|
return e == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isIntStrRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
|
||||||
|
|
||||||
// IsIntStr check if the string can convert to a integer.
|
// IsIntStr check if the string can convert to a integer.
|
||||||
func IsIntStr(s string) bool {
|
func IsIntStr(s string) bool {
|
||||||
match, _ := regexp.MatchString(`^[\+-]?\d+$`, s)
|
return isIntStrRegexMatcher.MatchString(s)
|
||||||
return match
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsIp check if the string is a ip address.
|
// IsIp check if the string is a ip address.
|
||||||
@@ -71,61 +72,61 @@ func IsIpV6(ipstr string) bool {
|
|||||||
return false
|
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.
|
// IsDns check if the string is dns.
|
||||||
func IsDns(dns string) bool {
|
func IsDns(dns string) bool {
|
||||||
pattern := `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`
|
return isDnsRegexMatcher.MatchString(dns)
|
||||||
reg := regexp.MustCompile(pattern)
|
|
||||||
return reg.MatchString(dns)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isEmailRegexMatcher *regexp.Regexp = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`)
|
||||||
|
|
||||||
// IsEmail check if the string is a email address.
|
// IsEmail check if the string is a email address.
|
||||||
func IsEmail(email string) bool {
|
func IsEmail(email string) bool {
|
||||||
pattern := `\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`
|
return isEmailRegexMatcher.MatchString(email)
|
||||||
reg := regexp.MustCompile(pattern)
|
|
||||||
return reg.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.
|
// IsChineseMobile check if the string is chinese mobile number.
|
||||||
func IsChineseMobile(mobileNum string) bool {
|
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}$"
|
return isChineseMobileRegexMatcher.MatchString(mobileNum)
|
||||||
reg := regexp.MustCompile(pattern)
|
|
||||||
return reg.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.
|
// IsChineseIdNum check if the string is chinese id number.
|
||||||
func IsChineseIdNum(id string) bool {
|
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]$`
|
return isChineseIdNumRegexMatcher.MatchString(id)
|
||||||
reg := regexp.MustCompile(pattern)
|
|
||||||
return reg.MatchString(id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var containChineseRegexMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
|
||||||
|
|
||||||
// ContainChinese check if the string contain mandarin chinese.
|
// ContainChinese check if the string contain mandarin chinese.
|
||||||
func ContainChinese(s string) bool {
|
func ContainChinese(s string) bool {
|
||||||
pattern := "[\u4e00-\u9fa5]"
|
return containChineseRegexMatcher.MatchString(s)
|
||||||
reg := regexp.MustCompile(pattern)
|
|
||||||
return reg.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.
|
// IsChinesePhone check if the string is chinese phone number.
|
||||||
// Valid chinese phone is xxx-xxxxxxxx or xxxx-xxxxxxx
|
// Valid chinese phone is xxx-xxxxxxxx or xxxx-xxxxxxx
|
||||||
func IsChinesePhone(phone string) bool {
|
func IsChinesePhone(phone string) bool {
|
||||||
pattern := `\d{3}-\d{8}|\d{4}-\d{7}`
|
return isChinesePhoneRegexMatcher.MatchString(phone)
|
||||||
reg := regexp.MustCompile(pattern)
|
|
||||||
return reg.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.
|
// IsCreditCard check if the string is credit card.
|
||||||
func IsCreditCard(creditCart string) bool {
|
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})$`
|
return isCreditCardRegexMatcher.MatchString(creditCart)
|
||||||
reg := regexp.MustCompile(pattern)
|
|
||||||
return reg.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.
|
// IsBase64 check if the string is base64 string.
|
||||||
func IsBase64(base64 string) bool {
|
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})$`
|
return isBase64RegexMatcher.MatchString(base64)
|
||||||
reg := regexp.MustCompile(pattern)
|
|
||||||
return reg.MatchString(base64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEmptyString check if the string is empty.
|
// IsEmptyString check if the string is empty.
|
||||||
|
|||||||
Reference in New Issue
Block a user