mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-06 05:42:25 +08:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7e5d946f1 | ||
|
|
687db4ce79 | ||
|
|
a9a4bb8841 | ||
|
|
bc6cb5f61b | ||
|
|
2c57266f8e | ||
|
|
57e49c9520 | ||
|
|
985c9a5d9a | ||
|
|
5cfb11f036 | ||
|
|
5b3d48a1e7 | ||
|
|
d0576e028f | ||
|
|
76bdec2b54 | ||
|
|
fa20aba3a7 | ||
|
|
7a4a429e23 | ||
|
|
a70ec6ad1e | ||
|
|
e435fa271b | ||
|
|
1533d00891 | ||
|
|
8b99641de0 | ||
|
|
251f899f18 | ||
|
|
00407e5182 | ||
|
|
4e457ad672 | ||
|
|
7d8d9c3543 | ||
|
|
1197e8d1b6 | ||
|
|
13bbe19ab2 | ||
|
|
2725575d2f | ||
|
|
037d2729ce | ||
|
|
09d98745b0 | ||
|
|
af5cfe6da1 | ||
|
|
d59259bbe0 | ||
|
|
3189628d54 | ||
|
|
62c5e251a5 | ||
|
|
6e6444c8c0 | ||
|
|
dd613e98b2 | ||
|
|
2d905ab03e |
12
README.md
12
README.md
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -19,7 +19,7 @@
|
||||
Lancet is a comprehensive, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js.
|
||||
</p>
|
||||
|
||||
English | [简体中文](./README_zh-CN.md) | [Website](https://uvdream.github.io/lancet-docs/en/)
|
||||
English | [简体中文](./README_zh-CN.md)
|
||||
|
||||
## Feature
|
||||
|
||||
@@ -38,10 +38,10 @@ English | [简体中文](./README_zh-CN.md) | [Website](https://uvdream.github.i
|
||||
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
|
||||
```
|
||||
|
||||
2. <b>For users who use version below go1.18, you should install v1.x.x. now latest v1 is v1.3.4. </b>
|
||||
2. <b>For users who use version below go1.18, you should install v1.x.x. now latest v1 is v1.3.5. </b>
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet@v1.3.3 // below go1.18, install latest version of v1.x.x
|
||||
go get github.com/duke-git/lancet@v1.3.5 // below go1.18, install latest version of v1.x.x
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -371,7 +371,6 @@ import "github.com/duke-git/lancet/v2/netutil"
|
||||
- [SendRequest](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#SendRequest)
|
||||
- [DecodeResponse](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#DecodeResponse)
|
||||
- [StructToUrlValues](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#StructToUrlValues)
|
||||
|
||||
- [HttpGet<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpGet)
|
||||
- [HttpDelete<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpDelete)
|
||||
- [HttpPost<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpPost)
|
||||
@@ -488,12 +487,14 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- [Capitalize](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Capitalize)
|
||||
- [IsString](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#IsString)
|
||||
- [KebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#KebabCase)
|
||||
- [UpperKebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#UpperKebabCase)
|
||||
- [LowerFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#LowerFirst)
|
||||
- [UpperFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#UpperFirst)
|
||||
- [PadEnd](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#PadEnd)
|
||||
- [PadStart](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#PadStart)
|
||||
- [Reverse](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Reverse)
|
||||
- [SnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#SnakeCase)
|
||||
- [UpperSnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#UpperSnakeCase)
|
||||
- [SplitEx](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#SplitEx)
|
||||
- [Wrap](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Wrap)
|
||||
- [Unwrap](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Unwrap)
|
||||
@@ -551,6 +552,7 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- [IsUrl](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsUrl)
|
||||
- [IsWeakPassword](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsWeakPassword)
|
||||
- [IsZeroValue](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsZeroValue)
|
||||
- [IsGBK](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsGBK)
|
||||
|
||||
### 20. xerror package implements helpers for errors.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -18,7 +18,7 @@
|
||||
lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
|
||||
</p>
|
||||
|
||||
简体中文 | [English](./README.md) | [文档](https://uvdream.github.io/lancet-docs)
|
||||
简体中文 | [English](./README.md)
|
||||
|
||||
## 特性
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
|
||||
```
|
||||
|
||||
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.3.4。</b>
|
||||
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.3.5。</b>
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet@v1.3.3 // 使用go1.18以下版本, 必须安装v1.x.x版本
|
||||
go get github.com/duke-git/lancet@v1.3.5 // 使用go1.18以下版本, 必须安装v1.x.x版本
|
||||
```
|
||||
|
||||
## 用法
|
||||
@@ -370,7 +370,6 @@ import "github.com/duke-git/lancet/v2/netutil"
|
||||
- [SendRequest](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#SendRequest)
|
||||
- [DecodeResponse](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#DecodeResponse)
|
||||
- [StructToUrlValues](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#StructToUrlValues)
|
||||
|
||||
- [HttpGet<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpGet)
|
||||
- [HttpDelete<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpDelete)
|
||||
- [HttpPost<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpPost)
|
||||
@@ -485,12 +484,14 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- [Capitalize](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Capitalize)
|
||||
- [IsString](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#IsString)
|
||||
- [KebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#KebabCase)
|
||||
- [UpperKebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#UpperKebabCase)
|
||||
- [LowerFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#LowerFirst)
|
||||
- [UpperFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#UpperFirst)
|
||||
- [PadEnd](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadEnd)
|
||||
- [PadStart](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadStart)
|
||||
- [Reverse](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Reverse)
|
||||
- [SnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#SnakeCase)
|
||||
- [UpperSnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#UpperSnakeCase)
|
||||
- [SplitEx](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#SplitEx)
|
||||
- [Wrap](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Wrap)
|
||||
- [Unwrap](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Unwrap)
|
||||
@@ -548,6 +549,7 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- [IsUrl](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsUrl)
|
||||
- [IsWeakPassword](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsWeakPassword)
|
||||
- [IsZeroValue](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsZeroValue)
|
||||
- [IsGBK](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsGBK)
|
||||
|
||||
### 20. xerror 包实现一些错误处理函数
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ func (c *Channel) Bridge(ctx context.Context, chanStream <-chan <-chan any) <-ch
|
||||
var stream <-chan any
|
||||
select {
|
||||
case maybeStream, ok := <-chanStream:
|
||||
if ok == false {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
stream = maybeStream
|
||||
|
||||
@@ -97,40 +97,43 @@ func ToString(value any) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch value.(type) {
|
||||
switch val := value.(type) {
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(value.(float32)), 'f', -1, 32)
|
||||
return strconv.FormatFloat(float64(val), 'f', -1, 32)
|
||||
case float64:
|
||||
return strconv.FormatFloat(value.(float64), 'f', -1, 64)
|
||||
return strconv.FormatFloat(val, 'f', -1, 64)
|
||||
case int:
|
||||
return strconv.FormatInt(int64(value.(int)), 10)
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int8:
|
||||
return strconv.FormatInt(int64(value.(int8)), 10)
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int16:
|
||||
return strconv.FormatInt(int64(value.(int16)), 10)
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int32:
|
||||
return strconv.FormatInt(int64(value.(int32)), 10)
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int64:
|
||||
return strconv.FormatInt(value.(int64), 10)
|
||||
return strconv.FormatInt(val, 10)
|
||||
case uint:
|
||||
return strconv.FormatUint(uint64(value.(uint)), 10)
|
||||
return strconv.FormatUint(uint64(val), 10)
|
||||
case uint8:
|
||||
return strconv.FormatUint(uint64(value.(uint8)), 10)
|
||||
return strconv.FormatUint(uint64(val), 10)
|
||||
case uint16:
|
||||
return strconv.FormatUint(uint64(value.(uint16)), 10)
|
||||
return strconv.FormatUint(uint64(val), 10)
|
||||
case uint32:
|
||||
return strconv.FormatUint(uint64(value.(uint32)), 10)
|
||||
return strconv.FormatUint(uint64(val), 10)
|
||||
case uint64:
|
||||
return strconv.FormatUint(value.(uint64), 10)
|
||||
return strconv.FormatUint(val, 10)
|
||||
case string:
|
||||
return value.(string)
|
||||
return val
|
||||
case []byte:
|
||||
return string(value.([]byte))
|
||||
return string(val)
|
||||
default:
|
||||
newValue, _ := json.Marshal(value)
|
||||
return string(newValue)
|
||||
b, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
|
||||
// todo: maybe we should't supprt other type convertion
|
||||
// todo: maybe we should't supprt other type conversion
|
||||
// v := reflect.ValueOf(value)
|
||||
// log.Panicf("Unsupported data type: %s ", v.String())
|
||||
// return ""
|
||||
|
||||
@@ -27,14 +27,9 @@ func TestToChannel(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToChannel")
|
||||
|
||||
ch := ToChannel([]int{1, 2, 3})
|
||||
val1, _ := <-ch
|
||||
assert.Equal(1, val1)
|
||||
|
||||
val2, _ := <-ch
|
||||
assert.Equal(2, val2)
|
||||
|
||||
val3, _ := <-ch
|
||||
assert.Equal(3, val3)
|
||||
assert.Equal(1, <-ch)
|
||||
assert.Equal(2, <-ch)
|
||||
assert.Equal(3, <-ch)
|
||||
|
||||
_, ok := <-ch
|
||||
assert.Equal(false, ok)
|
||||
@@ -254,6 +249,7 @@ func TestDecodeByte(t *testing.T) {
|
||||
|
||||
var obj string
|
||||
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
|
||||
DecodeByte(byteData, &obj)
|
||||
err := DecodeByte(byteData, &obj)
|
||||
assert.IsNil(err)
|
||||
assert.Equal("abc", obj)
|
||||
}
|
||||
|
||||
@@ -17,15 +17,19 @@ import (
|
||||
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesEcbEncrypt(data, key []byte) []byte {
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key))
|
||||
length := (len(data) + aes.BlockSize) / aes.BlockSize
|
||||
plain := make([]byte, length*aes.BlockSize)
|
||||
|
||||
copy(plain, data)
|
||||
|
||||
pad := byte(len(plain) - len(data))
|
||||
for i := len(data); i < len(plain); i++ {
|
||||
plain[i] = pad
|
||||
}
|
||||
|
||||
encrypted := make([]byte, len(plain))
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
}
|
||||
@@ -108,27 +112,32 @@ func AesCfbEncrypt(data, key []byte) []byte {
|
||||
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesCfbDecrypt decrypt data with key use AES CFB algorithm
|
||||
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32
|
||||
func AesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
if len(encrypted) < aes.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(key)
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
encrypted = encrypted[aes.BlockSize:]
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
|
||||
stream.XORKeyStream(encrypted, encrypted)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
@@ -139,6 +148,7 @@ func AesOfbEncrypt(data, key []byte) []byte {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
data = pkcs7Padding(data, aes.BlockSize)
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
@@ -148,6 +158,7 @@ func AesOfbEncrypt(data, key []byte) []byte {
|
||||
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
@@ -170,5 +181,6 @@ func AesOfbDecrypt(data, key []byte) []byte {
|
||||
mode.XORKeyStream(decrypted, data)
|
||||
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
|
||||
return decrypted
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
// DesEcbEncrypt encrypt data with key use DES ECB algorithm
|
||||
// len(key) should be 8
|
||||
func DesEcbEncrypt(data, key []byte) []byte {
|
||||
cipher, _ := des.NewCipher(generateDesKey(key))
|
||||
length := (len(data) + des.BlockSize) / des.BlockSize
|
||||
plain := make([]byte, length*des.BlockSize)
|
||||
copy(plain, data)
|
||||
@@ -26,6 +25,8 @@ func DesEcbEncrypt(data, key []byte) []byte {
|
||||
}
|
||||
|
||||
encrypted := make([]byte, len(plain))
|
||||
cipher, _ := des.NewCipher(generateDesKey(key))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
}
|
||||
@@ -59,6 +60,7 @@ func DesCbcEncrypt(data, key []byte) []byte {
|
||||
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,11 @@ func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pem.Encode(file, &block)
|
||||
err = pem.Encode(file, &block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file.Close()
|
||||
|
||||
// public key
|
||||
@@ -49,12 +53,16 @@ func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
|
||||
Bytes: derpText,
|
||||
}
|
||||
|
||||
//file,err = os.Create("rsa_public.pem")
|
||||
file, err = os.Create(pubKeyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pem.Encode(file, &block)
|
||||
|
||||
err = pem.Encode(file, &block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file.Close()
|
||||
|
||||
return nil
|
||||
@@ -72,7 +80,11 @@ func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
|
||||
}
|
||||
defer file.Close()
|
||||
buf := make([]byte, fileInfo.Size())
|
||||
file.Read(buf)
|
||||
|
||||
_, err = file.Read(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(buf)
|
||||
|
||||
@@ -101,7 +113,11 @@ func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
|
||||
}
|
||||
buf := make([]byte, fileInfo.Size())
|
||||
defer file.Close()
|
||||
file.Read(buf)
|
||||
|
||||
_, err = file.Read(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(buf)
|
||||
|
||||
|
||||
@@ -146,9 +146,9 @@ func (h *MaxHeap[T]) PrintStructure() {
|
||||
lastNum := powerTwo(level - 1)
|
||||
lastLen := lastNum + (lastNum - 1)
|
||||
|
||||
heapTree := make([][]string, level, level)
|
||||
heapTree := make([][]string, level)
|
||||
for i := 0; i < level; i++ {
|
||||
heapTree[i] = make([]string, lastLen, lastLen)
|
||||
heapTree[i] = make([]string, lastLen)
|
||||
for j := 0; j < lastLen; j++ {
|
||||
heapTree[i][j] = ""
|
||||
}
|
||||
@@ -169,9 +169,9 @@ func (h *MaxHeap[T]) PrintStructure() {
|
||||
for n := 0; n < lastLen; n++ {
|
||||
val := heapTree[m][n]
|
||||
if val == "" {
|
||||
fmt.Printf(" ")
|
||||
fmt.Print(" ")
|
||||
} else {
|
||||
fmt.Printf(val)
|
||||
fmt.Print(val)
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
@@ -30,8 +30,6 @@ func TestMaxHeap_BuildMaxHeap(t *testing.T) {
|
||||
assert.Equal(expected, heap.data)
|
||||
|
||||
assert.Equal(12, heap.Size())
|
||||
|
||||
heap.PrintStructure()
|
||||
}
|
||||
|
||||
func TestMaxHeap_Push(t *testing.T) {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/duke-git/lancet/v2/datastructure"
|
||||
)
|
||||
|
||||
// DoublyLink is a linked list. Whose node has a generic Value, Pre pointer points to a previous node of the link, Next pointer points to a next node of the link.
|
||||
// DoublyLink is a linked list. Whose node has a generic Value, Pre pointer points to a previous node of the dl, Next pointer points to a next node of the dl.
|
||||
type DoublyLink[T any] struct {
|
||||
Head *datastructure.LinkNode[T]
|
||||
length int
|
||||
@@ -19,30 +18,30 @@ func NewDoublyLink[T any]() *DoublyLink[T] {
|
||||
}
|
||||
|
||||
// InsertAtHead insert value into doubly linklist at head index
|
||||
func (link *DoublyLink[T]) InsertAtHead(value T) {
|
||||
func (dl *DoublyLink[T]) InsertAtHead(value T) {
|
||||
newNode := datastructure.NewLinkNode(value)
|
||||
size := link.Size()
|
||||
size := dl.Size()
|
||||
|
||||
if size == 0 {
|
||||
link.Head = newNode
|
||||
link.length++
|
||||
dl.Head = newNode
|
||||
dl.length++
|
||||
return
|
||||
}
|
||||
|
||||
newNode.Next = link.Head
|
||||
newNode.Next = dl.Head
|
||||
newNode.Pre = nil
|
||||
|
||||
link.Head.Pre = newNode
|
||||
link.Head = newNode
|
||||
dl.Head.Pre = newNode
|
||||
dl.Head = newNode
|
||||
|
||||
link.length++
|
||||
dl.length++
|
||||
}
|
||||
|
||||
// InsertAtTail insert value into doubly linklist at tail index
|
||||
func (link *DoublyLink[T]) InsertAtTail(value T) {
|
||||
current := link.Head
|
||||
func (dl *DoublyLink[T]) InsertAtTail(value T) {
|
||||
current := dl.Head
|
||||
if current == nil {
|
||||
link.InsertAtHead(value)
|
||||
dl.InsertAtHead(value)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,28 +54,29 @@ func (link *DoublyLink[T]) InsertAtTail(value T) {
|
||||
newNode.Pre = current
|
||||
current.Next = newNode
|
||||
|
||||
link.length++
|
||||
dl.length++
|
||||
}
|
||||
|
||||
// InsertAt insert value into doubly linklist at index
|
||||
func (link *DoublyLink[T]) InsertAt(index int, value T) error {
|
||||
size := link.length
|
||||
// param `index` should between [0, length], if index do not meet the conditions, do nothing
|
||||
func (dl *DoublyLink[T]) InsertAt(index int, value T) {
|
||||
size := dl.length
|
||||
if index < 0 || index > size {
|
||||
return errors.New("param index should between 0 and the length of doubly link.")
|
||||
return
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
link.InsertAtHead(value)
|
||||
return nil
|
||||
dl.InsertAtHead(value)
|
||||
return
|
||||
}
|
||||
|
||||
if index == size {
|
||||
link.InsertAtTail(value)
|
||||
return nil
|
||||
dl.InsertAtTail(value)
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
current := link.Head
|
||||
current := dl.Head
|
||||
|
||||
for current != nil {
|
||||
if i == index-1 {
|
||||
@@ -85,38 +85,36 @@ func (link *DoublyLink[T]) InsertAt(index int, value T) error {
|
||||
newNode.Pre = current
|
||||
|
||||
current.Next = newNode
|
||||
link.length++
|
||||
dl.length++
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
i++
|
||||
current = current.Next
|
||||
}
|
||||
|
||||
return errors.New("doubly link list no exist")
|
||||
}
|
||||
|
||||
// DeleteAtHead delete value in doubly linklist at head index
|
||||
func (link *DoublyLink[T]) DeleteAtHead() error {
|
||||
if link.Head == nil {
|
||||
return errors.New("doubly link list no exist")
|
||||
func (dl *DoublyLink[T]) DeleteAtHead() {
|
||||
if dl.Head == nil {
|
||||
return
|
||||
}
|
||||
current := link.Head
|
||||
link.Head = current.Next
|
||||
link.Head.Pre = nil
|
||||
link.length--
|
||||
|
||||
return nil
|
||||
current := dl.Head
|
||||
dl.Head = current.Next
|
||||
dl.Head.Pre = nil
|
||||
dl.length--
|
||||
}
|
||||
|
||||
// DeleteAtTail delete value in doubly linklist at tail index
|
||||
func (link *DoublyLink[T]) DeleteAtTail() error {
|
||||
if link.Head == nil {
|
||||
return errors.New("doubly link list no exist")
|
||||
// DeleteAtTail delete value in doubly linklist at tail
|
||||
func (dl *DoublyLink[T]) DeleteAtTail() {
|
||||
if dl.Head == nil {
|
||||
return
|
||||
}
|
||||
current := link.Head
|
||||
|
||||
current := dl.Head
|
||||
if current.Next == nil {
|
||||
return link.DeleteAtHead()
|
||||
dl.DeleteAtHead()
|
||||
}
|
||||
|
||||
for current.Next.Next != nil {
|
||||
@@ -124,45 +122,44 @@ func (link *DoublyLink[T]) DeleteAtTail() error {
|
||||
}
|
||||
|
||||
current.Next = nil
|
||||
link.length--
|
||||
return nil
|
||||
dl.length--
|
||||
}
|
||||
|
||||
// DeleteAt delete value in doubly linklist at index
|
||||
func (link *DoublyLink[T]) DeleteAt(index int) error {
|
||||
if link.Head == nil {
|
||||
return errors.New("doubly link list no exist")
|
||||
// param `index` should be [0, len(DoublyLink)-1]
|
||||
func (dl *DoublyLink[T]) DeleteAt(index int) {
|
||||
if dl.Head == nil {
|
||||
return
|
||||
}
|
||||
current := link.Head
|
||||
|
||||
current := dl.Head
|
||||
if current.Next == nil || index == 0 {
|
||||
return link.DeleteAtHead()
|
||||
dl.DeleteAtHead()
|
||||
}
|
||||
|
||||
if index == link.length-1 {
|
||||
return link.DeleteAtTail()
|
||||
if index == dl.length-1 {
|
||||
dl.DeleteAtTail()
|
||||
}
|
||||
|
||||
if index < 0 || index > link.length-1 {
|
||||
return errors.New("param index should between 0 and link size -1.")
|
||||
if index < 0 || index > dl.length-1 {
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
for current != nil {
|
||||
if i == index-1 {
|
||||
current.Next = current.Next.Next
|
||||
link.length--
|
||||
return nil
|
||||
dl.length--
|
||||
return
|
||||
}
|
||||
i++
|
||||
current = current.Next
|
||||
}
|
||||
|
||||
return errors.New("delete error")
|
||||
}
|
||||
|
||||
// Reverse the linked list
|
||||
func (link *DoublyLink[T]) Reverse() {
|
||||
current := link.Head
|
||||
func (dl *DoublyLink[T]) Reverse() {
|
||||
current := dl.Head
|
||||
var temp *datastructure.LinkNode[T]
|
||||
|
||||
for current != nil {
|
||||
@@ -173,20 +170,20 @@ func (link *DoublyLink[T]) Reverse() {
|
||||
}
|
||||
|
||||
if temp != nil {
|
||||
link.Head = temp.Pre
|
||||
dl.Head = temp.Pre
|
||||
}
|
||||
}
|
||||
|
||||
// GetMiddleNode return node at middle index of linked list
|
||||
func (link *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
|
||||
if link.Head == nil {
|
||||
func (dl *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
|
||||
if dl.Head == nil {
|
||||
return nil
|
||||
}
|
||||
if link.Head.Next == nil {
|
||||
return link.Head
|
||||
if dl.Head.Next == nil {
|
||||
return dl.Head
|
||||
}
|
||||
fast := link.Head
|
||||
slow := link.Head
|
||||
fast := dl.Head
|
||||
slow := dl.Head
|
||||
|
||||
for fast != nil {
|
||||
fast = fast.Next
|
||||
@@ -202,14 +199,14 @@ func (link *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
|
||||
}
|
||||
|
||||
// Size return the count of doubly linked list
|
||||
func (link *DoublyLink[T]) Size() int {
|
||||
return link.length
|
||||
func (dl *DoublyLink[T]) Size() int {
|
||||
return dl.length
|
||||
}
|
||||
|
||||
// Values return slice of all doubly linklist node value
|
||||
func (link *DoublyLink[T]) Values() []T {
|
||||
func (dl *DoublyLink[T]) Values() []T {
|
||||
result := []T{}
|
||||
current := link.Head
|
||||
current := dl.Head
|
||||
for current != nil {
|
||||
result = append(result, current.Value)
|
||||
current = current.Next
|
||||
@@ -218,8 +215,8 @@ func (link *DoublyLink[T]) Values() []T {
|
||||
}
|
||||
|
||||
// Print all nodes info of a linked list
|
||||
func (link *DoublyLink[T]) Print() {
|
||||
current := link.Head
|
||||
func (dl *DoublyLink[T]) Print() {
|
||||
current := dl.Head
|
||||
info := "[ "
|
||||
for current != nil {
|
||||
info += fmt.Sprintf("%+v, ", current)
|
||||
@@ -229,13 +226,13 @@ func (link *DoublyLink[T]) Print() {
|
||||
fmt.Println(info)
|
||||
}
|
||||
|
||||
// IsEmpty checks if link is empty or not
|
||||
func (link *DoublyLink[T]) IsEmpty() bool {
|
||||
return link.length == 0
|
||||
// IsEmpty checks if dl is empty or not
|
||||
func (dl *DoublyLink[T]) IsEmpty() bool {
|
||||
return dl.length == 0
|
||||
}
|
||||
|
||||
// Clear all nodes in doubly linklist
|
||||
func (link *DoublyLink[T]) Clear() {
|
||||
link.Head = nil
|
||||
link.length = 0
|
||||
func (dl *DoublyLink[T]) Clear() {
|
||||
dl.Head = nil
|
||||
dl.length = 0
|
||||
}
|
||||
|
||||
@@ -41,29 +41,24 @@ func TestDoublyLink_InsertAt(t *testing.T) {
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
|
||||
err := link.InsertAt(1, 1)
|
||||
assert.IsNotNil(err)
|
||||
link.InsertAt(1, 1) //do nothing
|
||||
|
||||
link.InsertAt(0, 1)
|
||||
link.InsertAt(1, 2)
|
||||
link.InsertAt(2, 4)
|
||||
link.InsertAt(2, 3)
|
||||
|
||||
link.Print()
|
||||
|
||||
expected := []int{1, 2, 3, 4}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
|
||||
}
|
||||
|
||||
func TestDoublyLink_DeleteAtHead(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtHead")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
err := link.DeleteAtHead()
|
||||
assert.IsNotNil(err)
|
||||
link.DeleteAtHead()
|
||||
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
@@ -71,7 +66,6 @@ func TestDoublyLink_DeleteAtHead(t *testing.T) {
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.DeleteAtHead()
|
||||
link.Print()
|
||||
|
||||
expected := []int{2, 3, 4}
|
||||
values := link.Values()
|
||||
@@ -83,8 +77,7 @@ func TestDoublyLink_DeleteAtTail(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtTail")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
err := link.DeleteAtTail()
|
||||
assert.IsNotNil(err)
|
||||
link.DeleteAtTail()
|
||||
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
@@ -92,7 +85,6 @@ func TestDoublyLink_DeleteAtTail(t *testing.T) {
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.DeleteAtTail()
|
||||
link.Print()
|
||||
|
||||
expected := []int{1, 2, 3}
|
||||
values := link.Values()
|
||||
@@ -104,8 +96,7 @@ func TestDoublyLink_DeleteAt(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAt")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
err := link.DeleteAt(0)
|
||||
assert.IsNotNil(err)
|
||||
link.DeleteAt(0)
|
||||
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
@@ -113,11 +104,7 @@ func TestDoublyLink_DeleteAt(t *testing.T) {
|
||||
link.InsertAtTail(4)
|
||||
link.InsertAtTail(5)
|
||||
|
||||
err = link.DeleteAt(5)
|
||||
assert.IsNotNil(err)
|
||||
|
||||
err = link.DeleteAt(0)
|
||||
assert.IsNil(err)
|
||||
link.DeleteAt(0)
|
||||
assert.Equal([]int{2, 3, 4, 5}, link.Values())
|
||||
|
||||
link.DeleteAt(3)
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/duke-git/lancet/v2/datastructure"
|
||||
)
|
||||
|
||||
// SinglyLink is a linked list. Whose node has a Value generics and Next pointer points to a next node of the link.
|
||||
// SinglyLink is a linked list. Whose node has a Value generics and Next pointer points to a next node of the sl.
|
||||
type SinglyLink[T any] struct {
|
||||
Head *datastructure.LinkNode[T]
|
||||
length int
|
||||
@@ -20,18 +19,18 @@ func NewSinglyLink[T any]() *SinglyLink[T] {
|
||||
}
|
||||
|
||||
// InsertAtHead insert value into singly linklist at head index
|
||||
func (link *SinglyLink[T]) InsertAtHead(value T) {
|
||||
func (sl *SinglyLink[T]) InsertAtHead(value T) {
|
||||
newNode := datastructure.NewLinkNode(value)
|
||||
newNode.Next = link.Head
|
||||
link.Head = newNode
|
||||
link.length++
|
||||
newNode.Next = sl.Head
|
||||
sl.Head = newNode
|
||||
sl.length++
|
||||
}
|
||||
|
||||
// InsertAtTail insert value into singly linklist at tail index
|
||||
func (link *SinglyLink[T]) InsertAtTail(value T) {
|
||||
current := link.Head
|
||||
func (sl *SinglyLink[T]) InsertAtTail(value T) {
|
||||
current := sl.Head
|
||||
if current == nil {
|
||||
link.InsertAtHead(value)
|
||||
sl.InsertAtHead(value)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -43,65 +42,63 @@ func (link *SinglyLink[T]) InsertAtTail(value T) {
|
||||
newNode.Next = nil
|
||||
current.Next = newNode
|
||||
|
||||
link.length++
|
||||
sl.length++
|
||||
}
|
||||
|
||||
// InsertAt insert value into singly linklist at index
|
||||
func (link *SinglyLink[T]) InsertAt(index int, value T) error {
|
||||
size := link.length
|
||||
// param `index` should between [0, len(SinglyLink)], if index do not meet the conditions, do nothing
|
||||
func (sl *SinglyLink[T]) InsertAt(index int, value T) {
|
||||
size := sl.length
|
||||
if index < 0 || index > size {
|
||||
return errors.New("param index should between 0 and the length of singly link.")
|
||||
return
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
link.InsertAtHead(value)
|
||||
return nil
|
||||
sl.InsertAtHead(value)
|
||||
return
|
||||
}
|
||||
|
||||
if index == size {
|
||||
link.InsertAtTail(value)
|
||||
return nil
|
||||
sl.InsertAtTail(value)
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
current := link.Head
|
||||
current := sl.Head
|
||||
|
||||
for current != nil {
|
||||
if i == index-1 {
|
||||
newNode := datastructure.NewLinkNode(value)
|
||||
newNode.Next = current.Next
|
||||
current.Next = newNode
|
||||
link.length++
|
||||
|
||||
return nil
|
||||
sl.length++
|
||||
return
|
||||
}
|
||||
i++
|
||||
current = current.Next
|
||||
}
|
||||
|
||||
return errors.New("singly link list no exist")
|
||||
}
|
||||
|
||||
// DeleteAtHead delete value in singly linklist at head index
|
||||
func (link *SinglyLink[T]) DeleteAtHead() error {
|
||||
if link.Head == nil {
|
||||
return errors.New("singly link list no exist")
|
||||
func (sl *SinglyLink[T]) DeleteAtHead() {
|
||||
if sl.Head == nil {
|
||||
return
|
||||
}
|
||||
current := link.Head
|
||||
link.Head = current.Next
|
||||
link.length--
|
||||
|
||||
return nil
|
||||
current := sl.Head
|
||||
sl.Head = current.Next
|
||||
sl.length--
|
||||
}
|
||||
|
||||
// DeleteAtTail delete value in singly linklist at tail index
|
||||
func (link *SinglyLink[T]) DeleteAtTail() error {
|
||||
if link.Head == nil {
|
||||
return errors.New("singly link list no exist")
|
||||
// DeleteAtTail delete value in singly linklist at tail
|
||||
func (sl *SinglyLink[T]) DeleteAtTail() {
|
||||
if sl.Head == nil {
|
||||
return
|
||||
}
|
||||
current := link.Head
|
||||
|
||||
current := sl.Head
|
||||
if current.Next == nil {
|
||||
return link.DeleteAtHead()
|
||||
sl.DeleteAtHead()
|
||||
}
|
||||
|
||||
for current.Next.Next != nil {
|
||||
@@ -109,68 +106,66 @@ func (link *SinglyLink[T]) DeleteAtTail() error {
|
||||
}
|
||||
|
||||
current.Next = nil
|
||||
link.length--
|
||||
return nil
|
||||
sl.length--
|
||||
}
|
||||
|
||||
// DeleteAt delete value in singly linklist at index
|
||||
func (link *SinglyLink[T]) DeleteAt(index int) error {
|
||||
if link.Head == nil {
|
||||
return errors.New("singly link list no exist")
|
||||
// param `index` should be [0, len(SinglyLink)-1]
|
||||
func (sl *SinglyLink[T]) DeleteAt(index int) {
|
||||
if sl.Head == nil {
|
||||
return
|
||||
}
|
||||
current := link.Head
|
||||
current := sl.Head
|
||||
if current.Next == nil || index == 0 {
|
||||
return link.DeleteAtHead()
|
||||
sl.DeleteAtHead()
|
||||
}
|
||||
|
||||
if index == link.length-1 {
|
||||
return link.DeleteAtTail()
|
||||
if index == sl.length-1 {
|
||||
sl.DeleteAtTail()
|
||||
}
|
||||
|
||||
if index < 0 || index > link.length-1 {
|
||||
return errors.New("param index should between 0 and link size -1.")
|
||||
if index < 0 || index > sl.length-1 {
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
for current != nil {
|
||||
if i == index-1 {
|
||||
current.Next = current.Next.Next
|
||||
link.length--
|
||||
return nil
|
||||
sl.length--
|
||||
return
|
||||
}
|
||||
i++
|
||||
current = current.Next
|
||||
}
|
||||
|
||||
return errors.New("delete error")
|
||||
}
|
||||
|
||||
// DeleteValue delete value in singly linklist
|
||||
func (link *SinglyLink[T]) DeleteValue(value T) {
|
||||
if link.Head == nil {
|
||||
func (sl *SinglyLink[T]) DeleteValue(value T) {
|
||||
if sl.Head == nil {
|
||||
return
|
||||
}
|
||||
dummyHead := datastructure.NewLinkNode(value)
|
||||
dummyHead.Next = link.Head
|
||||
dummyHead.Next = sl.Head
|
||||
current := dummyHead
|
||||
|
||||
for current.Next != nil {
|
||||
if reflect.DeepEqual(current.Next.Value, value) {
|
||||
current.Next = current.Next.Next
|
||||
link.length--
|
||||
sl.length--
|
||||
} else {
|
||||
current = current.Next
|
||||
}
|
||||
}
|
||||
|
||||
link.Head = dummyHead.Next
|
||||
sl.Head = dummyHead.Next
|
||||
}
|
||||
|
||||
// Reverse the linked list
|
||||
func (link *SinglyLink[T]) Reverse() {
|
||||
func (sl *SinglyLink[T]) Reverse() {
|
||||
var pre, next *datastructure.LinkNode[T]
|
||||
|
||||
current := link.Head
|
||||
current := sl.Head
|
||||
|
||||
for current != nil {
|
||||
next = current.Next
|
||||
@@ -179,19 +174,19 @@ func (link *SinglyLink[T]) Reverse() {
|
||||
current = next
|
||||
}
|
||||
|
||||
link.Head = pre
|
||||
sl.Head = pre
|
||||
}
|
||||
|
||||
// GetMiddleNode return node at middle index of linked list
|
||||
func (link *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
|
||||
if link.Head == nil {
|
||||
func (sl *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
|
||||
if sl.Head == nil {
|
||||
return nil
|
||||
}
|
||||
if link.Head.Next == nil {
|
||||
return link.Head
|
||||
if sl.Head.Next == nil {
|
||||
return sl.Head
|
||||
}
|
||||
fast := link.Head
|
||||
slow := link.Head
|
||||
fast := sl.Head
|
||||
slow := sl.Head
|
||||
|
||||
for fast != nil {
|
||||
fast = fast.Next
|
||||
@@ -207,14 +202,14 @@ func (link *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
|
||||
}
|
||||
|
||||
// Size return the count of singly linked list
|
||||
func (link *SinglyLink[T]) Size() int {
|
||||
return link.length
|
||||
func (sl *SinglyLink[T]) Size() int {
|
||||
return sl.length
|
||||
}
|
||||
|
||||
// Values return slice of all singly linklist node value
|
||||
func (link *SinglyLink[T]) Values() []T {
|
||||
func (sl *SinglyLink[T]) Values() []T {
|
||||
result := []T{}
|
||||
current := link.Head
|
||||
current := sl.Head
|
||||
for current != nil {
|
||||
result = append(result, current.Value)
|
||||
current = current.Next
|
||||
@@ -222,20 +217,20 @@ func (link *SinglyLink[T]) Values() []T {
|
||||
return result
|
||||
}
|
||||
|
||||
// IsEmpty checks if link is empty or not
|
||||
func (link *SinglyLink[T]) IsEmpty() bool {
|
||||
return link.length == 0
|
||||
// IsEmpty checks if sl is empty or not
|
||||
func (sl *SinglyLink[T]) IsEmpty() bool {
|
||||
return sl.length == 0
|
||||
}
|
||||
|
||||
// Clear all the node in singly linklist
|
||||
func (link *SinglyLink[T]) Clear() {
|
||||
link.Head = nil
|
||||
link.length = 0
|
||||
func (sl *SinglyLink[T]) Clear() {
|
||||
sl.Head = nil
|
||||
sl.length = 0
|
||||
}
|
||||
|
||||
// Print all nodes info of a linked list
|
||||
func (link *SinglyLink[T]) Print() {
|
||||
current := link.Head
|
||||
func (sl *SinglyLink[T]) Print() {
|
||||
current := sl.Head
|
||||
info := "[ "
|
||||
for current != nil {
|
||||
info += fmt.Sprintf("%+v, ", current)
|
||||
|
||||
@@ -41,25 +41,12 @@ func TestSinglyLink_InsertAt(t *testing.T) {
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
|
||||
err := link.InsertAt(1, 1)
|
||||
assert.IsNotNil(err)
|
||||
link.InsertAt(1, 1) //do nothing
|
||||
|
||||
err = link.InsertAt(0, 1)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
err = link.InsertAt(1, 2)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
err = link.InsertAt(2, 4)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
err = link.InsertAt(2, 3)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
link.InsertAt(0, 1)
|
||||
link.InsertAt(1, 2)
|
||||
link.InsertAt(2, 4)
|
||||
link.InsertAt(2, 3)
|
||||
|
||||
link.Print()
|
||||
|
||||
@@ -73,8 +60,8 @@ func TestSinglyLink_DeleteAtHead(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtHead")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
err := link.DeleteAtHead()
|
||||
assert.IsNotNil(err)
|
||||
|
||||
link.DeleteAtHead()
|
||||
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
@@ -94,8 +81,6 @@ func TestSinglyLink_DeleteAtTail(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtTail")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
err := link.DeleteAtTail()
|
||||
assert.IsNotNil(err)
|
||||
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
@@ -103,7 +88,6 @@ func TestSinglyLink_DeleteAtTail(t *testing.T) {
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.DeleteAtTail()
|
||||
link.Print()
|
||||
|
||||
expected := []int{1, 2, 3}
|
||||
values := link.Values()
|
||||
@@ -133,8 +117,6 @@ func TestSinglyLink_DeleteAt(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAt")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
err := link.DeleteAt(0)
|
||||
assert.IsNotNil(err)
|
||||
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
@@ -142,11 +124,7 @@ func TestSinglyLink_DeleteAt(t *testing.T) {
|
||||
link.InsertAtTail(4)
|
||||
link.InsertAtTail(5)
|
||||
|
||||
err = link.DeleteAt(5)
|
||||
assert.IsNotNil(err)
|
||||
|
||||
err = link.DeleteAt(0)
|
||||
assert.IsNil(err)
|
||||
link.DeleteAt(0)
|
||||
assert.Equal([]int{2, 3, 4, 5}, link.Values())
|
||||
|
||||
link.DeleteAt(3)
|
||||
@@ -167,7 +145,6 @@ func TestSinglyLink_Reverse(t *testing.T) {
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.Reverse()
|
||||
link.Print()
|
||||
assert.Equal([]int{4, 3, 2, 1}, link.Values())
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ func (l *List[T]) DeleteAt(index int) {
|
||||
return
|
||||
}
|
||||
if index == size-1 {
|
||||
data = append(data[:index])
|
||||
data = data[:index]
|
||||
} else {
|
||||
data = append(data[:index], data[index+1:]...)
|
||||
}
|
||||
@@ -174,7 +174,7 @@ func (l *List[T]) DeleteIf(f func(T) bool) int {
|
||||
continue
|
||||
}
|
||||
if index == size-1 {
|
||||
data = append(data[:index])
|
||||
data = data[:index]
|
||||
} else {
|
||||
data = append(data[:index], data[index+1:]...)
|
||||
index--
|
||||
@@ -221,7 +221,7 @@ func (l *List[T]) IsEmpty() bool {
|
||||
|
||||
// Clear the data of list
|
||||
func (l *List[T]) Clear() {
|
||||
l.data = make([]T, 0, 0)
|
||||
l.data = make([]T, 0)
|
||||
}
|
||||
|
||||
// Clone return a copy of list
|
||||
@@ -235,7 +235,7 @@ func (l *List[T]) Clone() *List[T] {
|
||||
// Merge two list, return new list, don't change original list
|
||||
func (l *List[T]) Merge(other *List[T]) *List[T] {
|
||||
l1, l2 := len(l.data), len(other.data)
|
||||
ml := NewList(make([]T, l1+l2, l1+l2))
|
||||
ml := NewList(make([]T, l1+l2))
|
||||
|
||||
data := append([]T{}, append(l.data, other.data...)...)
|
||||
ml.data = data
|
||||
@@ -274,7 +274,7 @@ func (l *List[T]) Unique() {
|
||||
data := l.data
|
||||
size := len(data)
|
||||
|
||||
uniqueData := make([]T, 0, 0)
|
||||
uniqueData := make([]T, 0)
|
||||
for i := 0; i < size; i++ {
|
||||
value := data[i]
|
||||
skip := true
|
||||
@@ -305,7 +305,7 @@ func (l *List[T]) Union(other *List[T]) *List[T] {
|
||||
|
||||
// Intersection creates a new list whose element both be contained in list l and other
|
||||
func (l *List[T]) Intersection(other *List[T]) *List[T] {
|
||||
result := NewList(make([]T, 0, 0))
|
||||
result := NewList(make([]T, 0))
|
||||
|
||||
for _, v := range l.data {
|
||||
if other.Contain(v) {
|
||||
|
||||
@@ -18,8 +18,6 @@ func TestArrayQueue_Enqueue(t *testing.T) {
|
||||
data := queue.Data()
|
||||
size := queue.Size()
|
||||
|
||||
queue.Print()
|
||||
|
||||
assert.Equal(expected, data)
|
||||
assert.Equal(3, size)
|
||||
}
|
||||
@@ -35,7 +33,6 @@ func TestArrayQueue_Dequeue(t *testing.T) {
|
||||
val, ok := queue.Dequeue()
|
||||
assert.Equal(true, ok)
|
||||
|
||||
queue.Print()
|
||||
assert.Equal(1, val)
|
||||
assert.Equal([]int{2, 3}, queue.Data())
|
||||
}
|
||||
@@ -50,8 +47,6 @@ func TestArrayQueue_Front(t *testing.T) {
|
||||
|
||||
val := queue.Front()
|
||||
|
||||
queue.Print()
|
||||
|
||||
assert.Equal(1, val)
|
||||
assert.Equal([]int{1, 2, 3}, queue.Data())
|
||||
}
|
||||
@@ -66,8 +61,6 @@ func TestArrayQueue_Back(t *testing.T) {
|
||||
|
||||
val := queue.Back()
|
||||
|
||||
queue.Print()
|
||||
|
||||
assert.Equal(3, val)
|
||||
assert.Equal([]int{1, 2, 3}, queue.Data())
|
||||
}
|
||||
|
||||
@@ -10,31 +10,43 @@ func TestCircularQueue_Enqueue(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Enqueue")
|
||||
|
||||
queue := NewCircularQueue[int](6)
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
queue.Enqueue(4)
|
||||
queue.Enqueue(5)
|
||||
|
||||
queue.Print()
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(4)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(5)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data())
|
||||
assert.Equal(5, queue.Size())
|
||||
|
||||
err := queue.Enqueue(6)
|
||||
err = queue.Enqueue(6)
|
||||
assert.IsNotNil(err)
|
||||
}
|
||||
|
||||
func TestCircularQueue_Dequeue(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_DeQueue")
|
||||
|
||||
queue := NewCircularQueue[int](6)
|
||||
queue := NewCircularQueue[int](4)
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
queue.Enqueue(4)
|
||||
queue.Enqueue(5)
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
val, err := queue.Dequeue()
|
||||
assert.IsNil(err)
|
||||
@@ -43,11 +55,7 @@ func TestCircularQueue_Dequeue(t *testing.T) {
|
||||
assert.Equal(false, queue.IsFull())
|
||||
|
||||
val, _ = queue.Dequeue()
|
||||
queue.Print()
|
||||
assert.Equal(2, *val)
|
||||
|
||||
queue.Enqueue(6)
|
||||
queue.Print()
|
||||
assert.Equal(false, queue.IsFull())
|
||||
}
|
||||
|
||||
@@ -55,55 +63,52 @@ func TestCircularQueue_Front(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Front")
|
||||
|
||||
queue := NewCircularQueue[int](6)
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
queue.Enqueue(4)
|
||||
queue.Enqueue(5)
|
||||
|
||||
queue.Print()
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
queue.Dequeue()
|
||||
queue.Dequeue()
|
||||
queue.Enqueue(6)
|
||||
queue.Enqueue(7)
|
||||
err = queue.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
queue.Print()
|
||||
err = queue.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
val := queue.Front()
|
||||
assert.Equal(3, val)
|
||||
assert.Equal(5, queue.Size())
|
||||
assert.IsNil(err)
|
||||
assert.Equal(1, val)
|
||||
assert.Equal(3, queue.Size())
|
||||
}
|
||||
|
||||
func TestCircularQueue_Back(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Back")
|
||||
|
||||
queue := NewCircularQueue[int](6)
|
||||
queue := NewCircularQueue[int](3)
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
queue.Enqueue(4)
|
||||
queue.Enqueue(5)
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
queue.Print()
|
||||
assert.Equal(5, queue.Back())
|
||||
err = queue.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
queue.Dequeue()
|
||||
queue.Dequeue()
|
||||
queue.Enqueue(6)
|
||||
queue.Enqueue(7)
|
||||
assert.Equal(2, queue.Back())
|
||||
|
||||
queue.Print()
|
||||
assert.Equal(7, queue.Back())
|
||||
val, _ := queue.Dequeue()
|
||||
assert.Equal(1, *val)
|
||||
|
||||
err = queue.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(3, queue.Back())
|
||||
}
|
||||
|
||||
func TestCircularQueue_Contain(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Contain")
|
||||
|
||||
queue := NewCircularQueue[int](2)
|
||||
queue.Enqueue(1)
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(true, queue.Contain(1))
|
||||
assert.Equal(false, queue.Contain(2))
|
||||
}
|
||||
@@ -115,7 +120,9 @@ func TestCircularQueue_Clear(t *testing.T) {
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
assert.Equal(0, queue.Size())
|
||||
|
||||
queue.Enqueue(1)
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(false, queue.IsEmpty())
|
||||
assert.Equal(1, queue.Size())
|
||||
|
||||
@@ -127,22 +134,12 @@ func TestCircularQueue_Clear(t *testing.T) {
|
||||
func TestCircularQueue_Data(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Data")
|
||||
|
||||
queue := NewCircularQueue[int](6)
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
queue.Enqueue(4)
|
||||
queue.Enqueue(5)
|
||||
queue := NewCircularQueue[int](3)
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
queue.Print()
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data())
|
||||
|
||||
queue.Dequeue()
|
||||
queue.Dequeue()
|
||||
queue.Enqueue(6)
|
||||
queue.Enqueue(7)
|
||||
|
||||
queue.Print()
|
||||
assert.Equal([]int{3, 4, 5, 6, 7}, queue.Data())
|
||||
err = queue.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal([]int{1, 2}, queue.Data())
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ func TestLinkedQueue_Enqueue(t *testing.T) {
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
queue.Print()
|
||||
|
||||
assert.Equal([]int{1, 2, 3}, queue.Data())
|
||||
assert.Equal(3, queue.Size())
|
||||
}
|
||||
|
||||
@@ -23,19 +23,24 @@ func TestPriorityQueue_Enqueue(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPriorityQueue_Enqueue")
|
||||
|
||||
comparator := &intComparator{}
|
||||
pq := NewPriorityQueue[int](10, comparator)
|
||||
pq := NewPriorityQueue[int](3, comparator)
|
||||
|
||||
assert.Equal(true, pq.IsEmpty())
|
||||
assert.Equal(false, pq.IsFull())
|
||||
|
||||
for i := 1; i < 11; i++ {
|
||||
pq.Enqueue(i)
|
||||
}
|
||||
err := pq.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = pq.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = pq.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(true, pq.IsFull())
|
||||
|
||||
queueData := pq.Data()
|
||||
assert.Equal([]int{10, 9, 6, 7, 8, 2, 5, 1, 4, 3}, queueData)
|
||||
assert.Equal([]int{3, 1, 2}, queueData)
|
||||
|
||||
}
|
||||
|
||||
@@ -43,22 +48,23 @@ func TestPriorityQueue_Dequeue(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPriorityQueue_Dequeue")
|
||||
|
||||
comparator := &intComparator{}
|
||||
pq := NewPriorityQueue[int](10, comparator)
|
||||
pq := NewPriorityQueue[int](3, comparator)
|
||||
|
||||
_, ok := pq.Dequeue()
|
||||
assert.Equal(false, ok)
|
||||
|
||||
for i := 1; i < 11; i++ {
|
||||
pq.Enqueue(i)
|
||||
}
|
||||
err := pq.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(10, pq.Size())
|
||||
err = pq.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = pq.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(3, pq.Size())
|
||||
|
||||
val, ok := pq.Dequeue()
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal(10, val)
|
||||
|
||||
assert.Equal([]int{9, 8, 6, 7, 3, 2, 5, 1, 4}, pq.Data())
|
||||
|
||||
assert.Equal(9, pq.Size())
|
||||
assert.Equal(3, val)
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ func TestLinkedStack_Push(t *testing.T) {
|
||||
stack.Push(2)
|
||||
stack.Push(3)
|
||||
|
||||
stack.Print()
|
||||
|
||||
expected := []int{3, 2, 1}
|
||||
values := stack.Data()
|
||||
size := stack.Size()
|
||||
|
||||
@@ -27,8 +27,6 @@ func TestBSTree_Insert(t *testing.T) {
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
bstree.Print()
|
||||
}
|
||||
|
||||
func TestBSTree_PreOrderTraverse(t *testing.T) {
|
||||
@@ -86,8 +84,6 @@ func TestBSTree_LevelOrderTraverse(t *testing.T) {
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
bstree.Print()
|
||||
|
||||
acturl := bstree.LevelOrderTraverse()
|
||||
t.Log(acturl)
|
||||
assert.Equal([]int{6, 5, 7, 2, 4}, acturl)
|
||||
@@ -103,10 +99,8 @@ func TestBSTree_Delete(t *testing.T) {
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
bstree.Print()
|
||||
|
||||
bstree.Delete(4)
|
||||
bstree.Print()
|
||||
|
||||
acturl1 := bstree.InOrderTraverse()
|
||||
t.Log(acturl1)
|
||||
assert.Equal([]int{2, 5, 6, 7}, acturl1)
|
||||
@@ -129,8 +123,6 @@ func TestBSTree_Depth(t *testing.T) {
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
bstree.Print()
|
||||
|
||||
assert.Equal(bstree.Depth(), 4)
|
||||
}
|
||||
|
||||
@@ -150,8 +142,6 @@ func TestBSTree_IsSubTree(t *testing.T) {
|
||||
subTree.Insert(4)
|
||||
subTree.Insert(6)
|
||||
|
||||
subTree.Print()
|
||||
|
||||
assert.Equal(true, superTree.HasSubTree(subTree))
|
||||
assert.Equal(false, subTree.HasSubTree(superTree))
|
||||
}
|
||||
|
||||
@@ -38,35 +38,35 @@ func inOrderTraverse[T any](node *datastructure.TreeNode[T]) []T {
|
||||
return data
|
||||
}
|
||||
|
||||
func preOrderPrint[T any](node *datastructure.TreeNode[T]) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
// func preOrderPrint[T any](node *datastructure.TreeNode[T]) {
|
||||
// if node == nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
fmt.Printf("%v, ", node.Value)
|
||||
preOrderPrint(node.Left)
|
||||
preOrderPrint(node.Right)
|
||||
}
|
||||
// fmt.Printf("%v, ", node.Value)
|
||||
// preOrderPrint(node.Left)
|
||||
// preOrderPrint(node.Right)
|
||||
// }
|
||||
|
||||
func postOrderPrint[T any](node *datastructure.TreeNode[T]) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
// func postOrderPrint[T any](node *datastructure.TreeNode[T]) {
|
||||
// if node == nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
preOrderPrint(node.Left)
|
||||
preOrderPrint(node.Right)
|
||||
fmt.Printf("%v, ", node.Value)
|
||||
}
|
||||
// postOrderPrint(node.Left)
|
||||
// postOrderPrint(node.Right)
|
||||
// fmt.Printf("%v, ", node.Value)
|
||||
// }
|
||||
|
||||
func inOrderPrint[T any](node *datastructure.TreeNode[T]) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
// func inOrderPrint[T any](node *datastructure.TreeNode[T]) {
|
||||
// if node == nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
inOrderPrint(node.Left)
|
||||
fmt.Printf("%v, ", node.Value)
|
||||
inOrderPrint(node.Right)
|
||||
}
|
||||
// inOrderPrint(node.Left)
|
||||
// fmt.Printf("%v, ", node.Value)
|
||||
// inOrderPrint(node.Right)
|
||||
// }
|
||||
|
||||
func levelOrderTraverse[T any](root *datastructure.TreeNode[T], traversal *[]T) {
|
||||
var q []*datastructure.TreeNode[T] // queue
|
||||
|
||||
@@ -132,12 +132,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="SinglyLink_InsertAt">InsertAt</span>
|
||||
<p>Insert value into singly linklist at index, index shoud be great or equal 0 and less or equal number of link nodes</p>
|
||||
<p>Insert value into singly linklist at index, param `index` should between [0, len(SinglyLink)], if index do not meet the conditions, do nothing</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (link *SinglyLink[T]) InsertAt(index int, value T) error
|
||||
func (link *SinglyLink[T]) InsertAt(index int, value T)
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -152,6 +152,8 @@ import (
|
||||
func main() {
|
||||
lk := link.NewSinglyLink[int]()
|
||||
|
||||
lk.InsertAt(1, 1) //do nothing
|
||||
|
||||
lk.InsertAt(0, 1)
|
||||
lk.InsertAt(1, 2)
|
||||
lk.InsertAt(2, 3)
|
||||
@@ -228,12 +230,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="SinglyLink_DeleteAt">DeleteAt</span>
|
||||
<p>Delete value at specific index, index shoud be great or equal 0 and less or less than number of link nodes - 1</p>
|
||||
<p>Delete value at specific index, param `index` should be [0, len(SinglyLink)-1]</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (link *SinglyLink[T]) DeleteAt(index int) error
|
||||
func (link *SinglyLink[T]) DeleteAt(index int)
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -253,9 +255,8 @@ func main() {
|
||||
lk.InsertAtTail(3)
|
||||
lk.InsertAtTail(4)
|
||||
|
||||
err := lk.DeleteAt(3)
|
||||
lk.DeleteAt(3)
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{1, 2, 3}
|
||||
}
|
||||
```
|
||||
@@ -268,7 +269,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (link *SinglyLink[T]) DeleteAtHead() error
|
||||
func (link *SinglyLink[T]) DeleteAtHead()
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -288,9 +289,8 @@ func main() {
|
||||
lk.InsertAtTail(3)
|
||||
lk.InsertAtTail(4)
|
||||
|
||||
err := lk.DeleteAtHead()
|
||||
lk.DeleteAtHead()
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{2, 3, 4}
|
||||
}
|
||||
```
|
||||
@@ -304,7 +304,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (link *SinglyLink[T]) DeleteAtTail() error
|
||||
func (link *SinglyLink[T]) DeleteAtTail()
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -323,9 +323,8 @@ func main() {
|
||||
lk.InsertAtTail(2)
|
||||
lk.InsertAtTail(3)
|
||||
|
||||
err := lk.DeleteAtTail()
|
||||
lk.DeleteAtTail()
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{1, 2}
|
||||
}
|
||||
```
|
||||
@@ -628,12 +627,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="DoublyLink_InsertAt">InsertAt</span>
|
||||
<p>Insert value into doubly linklist at index, index shoud be great or equal 0 and less or equal number of link nodes</p>
|
||||
<p>Insert value into doubly linklist at index, param `index` should between [0, len(DoublyLink)], if index do not meet the conditions, do nothing</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (link *DoublyLink[T]) InsertAt(index int, value T) error
|
||||
func (link *DoublyLink[T]) InsertAt(index int, value T)
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -648,6 +647,8 @@ import (
|
||||
func main() {
|
||||
lk := link.NewDoublyLink[int]()
|
||||
|
||||
lk.InsertAt(1, 1) //do nothing
|
||||
|
||||
lk.InsertAt(0, 1)
|
||||
lk.InsertAt(1, 2)
|
||||
lk.InsertAt(2, 3)
|
||||
@@ -724,12 +725,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="DoublyLink_DeleteAt">DeleteAt</span>
|
||||
<p>Delete value at specific index, index shoud be great or equal 0 and less or less than number of link nodes - 1</p>
|
||||
<p>Delete value at specific index, param `index` should be [0, len(DoublyLink)-1]</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (link *DoublyLink[T]) DeleteAt(index int) error
|
||||
func (link *DoublyLink[T]) DeleteAt(index int)
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -749,9 +750,8 @@ func main() {
|
||||
lk.InsertAtTail(3)
|
||||
lk.InsertAtTail(4)
|
||||
|
||||
err := lk.DeleteAt(3)
|
||||
lk.DeleteAt(3)
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{1, 2, 3}
|
||||
}
|
||||
```
|
||||
@@ -764,7 +764,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (link *DoublyLink[T]) DeleteAtHead() error
|
||||
func (link *DoublyLink[T]) DeleteAtHead()
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -784,9 +784,8 @@ func main() {
|
||||
lk.InsertAtTail(3)
|
||||
lk.InsertAtTail(4)
|
||||
|
||||
err := lk.DeleteAtHead()
|
||||
lk.DeleteAtHead()
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{2, 3, 4}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -132,12 +132,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="SinglyLink_InsertAt">InsertAt</span>
|
||||
<p>将值插入到索引处的链表中,索引应大于或等于 0 且小于或等于链表节点数</p>
|
||||
<p>将值插入到索引处的链表中,索引应大于或等于0且小于或等于链表节点数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (link *SinglyLink[T]) InsertAt(index int, value T) error
|
||||
func (link *SinglyLink[T]) InsertAt(index int, value T)
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -152,6 +152,8 @@ import (
|
||||
func main() {
|
||||
lk := link.NewSinglyLink[int]()
|
||||
|
||||
lk.InsertAt(1, 1) //do nothing
|
||||
|
||||
lk.InsertAt(0, 1)
|
||||
lk.InsertAt(1, 2)
|
||||
lk.InsertAt(2, 3)
|
||||
@@ -228,12 +230,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="SinglyLink_DeleteAt">DeleteAt</span>
|
||||
<p>删除特定索引处的值,索引应大于或等于0且小于或等于链接节点数 - 1</p>
|
||||
<p>删除特定索引处的值,索引应大于或等于0且小于或等于链接节点数-1</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (link *SinglyLink[T]) DeleteAt(index int) error
|
||||
func (link *SinglyLink[T]) DeleteAt(index int)
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -253,9 +255,8 @@ func main() {
|
||||
lk.InsertAtTail(3)
|
||||
lk.InsertAtTail(4)
|
||||
|
||||
err := lk.DeleteAt(3)
|
||||
lk.DeleteAt(3)
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{1, 2, 3}
|
||||
}
|
||||
```
|
||||
@@ -268,7 +269,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (link *SinglyLink[T]) DeleteAtHead() error
|
||||
func (link *SinglyLink[T]) DeleteAtHead()
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -288,9 +289,8 @@ func main() {
|
||||
lk.InsertAtTail(3)
|
||||
lk.InsertAtTail(4)
|
||||
|
||||
err := lk.DeleteAtHead()
|
||||
lk.DeleteAtHead()
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{2, 3, 4}
|
||||
}
|
||||
```
|
||||
@@ -304,7 +304,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (link *SinglyLink[T]) DeleteAtTail() error
|
||||
func (link *SinglyLink[T]) DeleteAtTail()
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -323,9 +323,8 @@ func main() {
|
||||
lk.InsertAtTail(2)
|
||||
lk.InsertAtTail(3)
|
||||
|
||||
err := lk.DeleteAtTail()
|
||||
lk.DeleteAtTail()
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{1, 2}
|
||||
}
|
||||
```
|
||||
@@ -628,12 +627,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="DoublyLink_InsertAt">InsertAt</span>
|
||||
<p>将值插入到索引处的链表中,索引应大于或等于 0 且小于或等于链表节点数</p>
|
||||
<p>将值插入到索引处的链表中,索引应大于或等于0且小于或等于链表节点数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (link *DoublyLink[T]) InsertAt(index int, value T) error
|
||||
func (link *DoublyLink[T]) InsertAt(index int, value T)
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -648,6 +647,8 @@ import (
|
||||
func main() {
|
||||
lk := link.NewDoublyLink[int]()
|
||||
|
||||
lk.InsertAt(1, 1) //do nothing
|
||||
|
||||
lk.InsertAt(0, 1)
|
||||
lk.InsertAt(1, 2)
|
||||
lk.InsertAt(2, 3)
|
||||
@@ -724,12 +725,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="DoublyLink_DeleteAt">DeleteAt</span>
|
||||
<p>删除特定索引处的值,索引应大于或等于0且小于或等于链接节点数 - 1</p>
|
||||
<p>删除特定索引处的值,索引应大于或等于0且小于或等于链接节点数-1</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (link *DoublyLink[T]) DeleteAt(index int) error
|
||||
func (link *DoublyLink[T]) DeleteAt(index int)
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -749,9 +750,8 @@ func main() {
|
||||
lk.InsertAtTail(3)
|
||||
lk.InsertAtTail(4)
|
||||
|
||||
err := lk.DeleteAt(3)
|
||||
lk.DeleteAt(3)
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{1, 2, 3}
|
||||
}
|
||||
```
|
||||
@@ -764,7 +764,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (link *DoublyLink[T]) DeleteAtHead() error
|
||||
func (link *DoublyLink[T]) DeleteAtHead()
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -784,9 +784,8 @@ func main() {
|
||||
lk.InsertAtTail(3)
|
||||
lk.InsertAtTail(4)
|
||||
|
||||
err := lk.DeleteAtHead()
|
||||
lk.DeleteAtHead()
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{2, 3, 4}
|
||||
}
|
||||
```
|
||||
@@ -800,7 +799,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (link *DoublyLink[T]) DeleteAtTail() error
|
||||
func (link *DoublyLink[T]) DeleteAtTail()
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -819,9 +818,8 @@ func main() {
|
||||
lk.InsertAtTail(2)
|
||||
lk.InsertAtTail(3)
|
||||
|
||||
err := lk.DeleteAtTail()
|
||||
lk.DeleteAtTail()
|
||||
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(lk.Values()) //[]int{1, 2}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -28,13 +28,12 @@ import (
|
||||
|
||||
|
||||
### <span id="Comma">Comma</span>
|
||||
<p>Add comma to number by every 3 numbers from right. ahead by symbol char.
|
||||
Param should be number or numberic string.</p>
|
||||
<p>Add comma to a number value by every 3 numbers from right to left. ahead by symbol char. if value is a invalid number string like "aa", return empty string.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Comma(v any, symbol string) string
|
||||
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
|
||||
@@ -28,12 +28,12 @@ import (
|
||||
|
||||
|
||||
### <span id="Comma">Comma</span>
|
||||
<p>用逗号每隔3位分割数字/字符串,签名添加符号。参数必须是数字或者可以转为数字的字符串</p>
|
||||
<p>用逗号每隔3位分割数字/字符串,支持前缀添加符号。参数value必须是数字或者可以转为数字的字符串, 否则返回空字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Comma(v any, symbol string) string
|
||||
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ import (
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Average[T lancetconstraints.Number](numbers ...T) T
|
||||
func Average[T constraints.Integer | constraints.Float](numbers ...T) T
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -157,7 +157,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Max[T lancetconstraints.Number](numbers ...T) T
|
||||
func Max[T constraints.Integer | constraints.Float](numbers ...T) T
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -223,7 +223,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Min[T lancetconstraints.Number](numbers ...T) T
|
||||
func Min[T constraints.Integer | constraints.Float](numbers ...T) T
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ import (
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Average[T lancetconstraints.Number](numbers ...T) T
|
||||
func Average[T constraints.Integer | constraints.Float](numbers ...T) T
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -154,7 +154,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Max[T lancetconstraints.Number](numbers ...T) T
|
||||
func Max[T constraints.Integer | constraints.Float](numbers ...T) T
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -220,7 +220,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Min[T lancetconstraints.Number](numbers ...T) T
|
||||
func Min[T constraints.Integer | constraints.Float](numbers ...T) T
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
|
||||
@@ -1138,7 +1138,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Sort[T lancetconstraints.Ordered](slice []T, sortOrder ...string)
|
||||
func Sort[T constraints.Ordered](slice []T, sortOrder ...string)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -1135,7 +1135,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Sort[T lancetconstraints.Ordered](slice []T, sortOrder ...string)
|
||||
func Sort[T constraints.Ordered](slice []T, sortOrder ...string)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
116
docs/strutil.md
116
docs/strutil.md
@@ -28,12 +28,14 @@ import (
|
||||
- [Capitalize](#Capitalize)
|
||||
- [IsString](#IsString)
|
||||
- [KebabCase](#KebabCase)
|
||||
- [UpperKebabCase](#UpperKebabCase)
|
||||
- [LowerFirst](#LowerFirst)
|
||||
- [UpperFirst](#UpperFirst)
|
||||
- [PadEnd](#PadEnd)
|
||||
- [PadStart](#PadStart)
|
||||
- [Reverse](#Reverse)
|
||||
- [SnakeCase](#SnakeCase)
|
||||
- [UpperSnakeCase](#UpperSnakeCase)
|
||||
- [SplitEx](#SplitEx)
|
||||
- [Wrap](#Wrap)
|
||||
- [Unwrap](#Unwrap)
|
||||
@@ -165,10 +167,8 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="CamelCase">CamelCase</span>
|
||||
<p>Covert string to camelCase string.</p>
|
||||
<p>Coverts string to camelCase string, non letters and numbers will be ignored.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
@@ -195,6 +195,75 @@ func main() {
|
||||
|
||||
s4 := strutil.CamelCase("foo bar")
|
||||
fmt.Println(s4) //fooBar
|
||||
|
||||
s4 := strutil.CamelCase("Foo-#1😄$_%^&*(1bar")
|
||||
fmt.Println(s4) //foo11Bar
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="KebabCase">KebabCase</span>
|
||||
<p>KebabCase covert string to kebab-case, non letters and numbers will be ignored.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func KebabCase(s string) string
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s1 := strutil.KebabCase("Foo Bar-")
|
||||
fmt.Println(s1) //foo-bar
|
||||
|
||||
s2 := strutil.KebabCase("foo_Bar")
|
||||
fmt.Println(s2) //foo-bar
|
||||
|
||||
s3 := strutil.KebabCase("fooBar")
|
||||
fmt.Println(s3) //foo-bar
|
||||
|
||||
s4 := strutil.KebabCase("__FOO_BAR__")
|
||||
fmt.Println(s4) //foo-bar
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="UpperKebabCase">UpperKebabCase</span>
|
||||
<p>UpperKebabCase covert string to upper KEBAB-CASE, non letters and numbers will be ignored.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func KebabCase(s string) string
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s1 := strutil.UpperKebabCase("Foo Bar-")
|
||||
fmt.Println(s1) //FOO-BAR
|
||||
|
||||
s2 := strutil.UpperKebabCase("foo_Bar")
|
||||
fmt.Println(s2) //FOO-BAR
|
||||
|
||||
s3 := strutil.UpperKebabCase("fooBar")
|
||||
fmt.Println(s3) //FOO-BAR
|
||||
|
||||
s4 := strutil.UpperKebabCase("__FOO_BAR__")
|
||||
fmt.Println(s4) //FOO-BAR
|
||||
}
|
||||
```
|
||||
|
||||
@@ -457,7 +526,7 @@ func main() {
|
||||
|
||||
|
||||
### <span id="SnakeCase">SnakeCase</span>
|
||||
<p>Covert string to snake_case.</p>
|
||||
<p>Coverts string to snake_case, non letters and numbers will be ignored.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
@@ -483,14 +552,47 @@ func main() {
|
||||
fmt.Println(s3) //foo_bar
|
||||
|
||||
s4 := strutil.SnakeCase("__FOO_BAR__")
|
||||
fmt.Println(s4) //f_o_o_b_a_r
|
||||
fmt.Println(s4) //foo_bar
|
||||
|
||||
s5 := strutil.SnakeCase("aBbc-s$@a&%_B.B^C")
|
||||
fmt.Println(s5) //a_bbc_s_a_b_b_c
|
||||
s5 := strutil.SnakeCase("Foo-#1😄$_%^&*(1bar")
|
||||
fmt.Println(s5) //foo_1_1_bar
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="UpperSnakeCase">UpperSnakeCase</span>
|
||||
<p>Coverts string to upper KEBAB-CASE, non letters and numbers will be ignored.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func SnakeCase(s string) string
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s1 := strutil.UpperSnakeCase("Foo Bar-")
|
||||
fmt.Println(s1) //FOO_BAR
|
||||
|
||||
s2 := strutil.UpperSnakeCase("foo_Bar")
|
||||
fmt.Println(s2) //FOO_BAR
|
||||
|
||||
s3 := strutil.UpperSnakeCase("fooBar")
|
||||
fmt.Println(s3) //FOO_BAR
|
||||
|
||||
s4 := strutil.UpperSnakeCase("__FOO_BAR__")
|
||||
fmt.Println(s4) //FOO_BAR
|
||||
|
||||
s5 := strutil.UpperSnakeCase("Foo-#1😄$_%^&*(1bar")
|
||||
fmt.Println(s5) //FOO_1_1_BAR
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="SplitEx">SplitEx</span>
|
||||
|
||||
@@ -28,12 +28,14 @@ import (
|
||||
- [Capitalize](#Capitalize)
|
||||
- [IsString](#IsString)
|
||||
- [KebabCase](#KebabCase)
|
||||
- [UpperKebabCase](#UpperKebabCase)
|
||||
- [LowerFirst](#LowerFirst)
|
||||
- [UpperFirst](#UpperFirst)
|
||||
- [PadEnd](#PadEnd)
|
||||
- [PadStart](#PadStart)
|
||||
- [Reverse](#Reverse)
|
||||
- [SnakeCase](#SnakeCase)
|
||||
- [UpperSnakeCase](#UpperSnakeCase)
|
||||
- [SplitEx](#SplitEx)
|
||||
- [Wrap](#Wrap)
|
||||
- [Unwrap](#Unwrap)
|
||||
@@ -169,7 +171,7 @@ func main() {
|
||||
|
||||
|
||||
### <span id="CamelCase">CamelCase</span>
|
||||
<p>将字符串转换为驼峰式字符串</p>
|
||||
<p>将字符串转换为驼峰式字符串, 非字母和数字会被忽略</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -196,6 +198,9 @@ func main() {
|
||||
|
||||
s4 := strutil.CamelCase("foo bar")
|
||||
fmt.Println(s4) //fooBar
|
||||
|
||||
s4 := strutil.CamelCase("Foo-#1😄$_%^&*(1bar")
|
||||
fmt.Println(s4) //foo11Bar
|
||||
}
|
||||
```
|
||||
|
||||
@@ -261,7 +266,7 @@ func main() {
|
||||
|
||||
|
||||
### <span id="KebabCase">KebabCase</span>
|
||||
<p>将字符串转换为kebab-case</p>
|
||||
<p>将字符串转换为kebab-case, 非字母和数字会被忽略</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -287,7 +292,40 @@ func main() {
|
||||
fmt.Println(s3) //foo-bar
|
||||
|
||||
s4 := strutil.KebabCase("__FOO_BAR__")
|
||||
fmt.Println(s4) //f-o-o-b-a-r
|
||||
fmt.Println(s4) //foo-bar
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="UpperKebabCase">UpperKebabCase</span>
|
||||
<p>将字符串转换为大写KEBAB-CASE, 非字母和数字会被忽略</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func KebabCase(s string) string
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s1 := strutil.UpperKebabCase("Foo Bar-")
|
||||
fmt.Println(s1) //FOO-BAR
|
||||
|
||||
s2 := strutil.UpperKebabCase("foo_Bar")
|
||||
fmt.Println(s2) //FOO-BAR
|
||||
|
||||
s3 := strutil.UpperKebabCase("fooBar")
|
||||
fmt.Println(s3) //FOO-BAR
|
||||
|
||||
s4 := strutil.UpperKebabCase("__FOO_BAR__")
|
||||
fmt.Println(s4) //FOO-BAR
|
||||
}
|
||||
```
|
||||
|
||||
@@ -458,7 +496,7 @@ func main() {
|
||||
|
||||
|
||||
### <span id="SnakeCase">SnakeCase</span>
|
||||
<p>将字符串转换为snake_case形式</p>
|
||||
<p>将字符串转换为snake_case形式, 非字母和数字会被忽略</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -484,10 +522,45 @@ func main() {
|
||||
fmt.Println(s3) //foo_bar
|
||||
|
||||
s4 := strutil.SnakeCase("__FOO_BAR__")
|
||||
fmt.Println(s4) //f_o_o_b_a_r
|
||||
fmt.Println(s4) //foo_bar
|
||||
|
||||
s5 := strutil.SnakeCase("aBbc-s$@a&%_B.B^C")
|
||||
fmt.Println(s5) //a_bbc_s_a_b_b_c
|
||||
s5 := strutil.SnakeCase("Foo-#1😄$_%^&*(1bar")
|
||||
fmt.Println(s5) //foo_1_1_bar
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="UpperSnakeCase">UpperSnakeCase</span>
|
||||
<p>将字符串转换为大写SNAKE_CASE形式, 非字母和数字会被忽略</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func SnakeCase(s string) string
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s1 := strutil.UpperSnakeCase("Foo Bar-")
|
||||
fmt.Println(s1) //FOO_BAR
|
||||
|
||||
s2 := strutil.UpperSnakeCase("foo_Bar")
|
||||
fmt.Println(s2) //FOO_BAR
|
||||
|
||||
s3 := strutil.UpperSnakeCase("fooBar")
|
||||
fmt.Println(s3) //FOO_BAR
|
||||
|
||||
s4 := strutil.UpperSnakeCase("__FOO_BAR__")
|
||||
fmt.Println(s4) //FOO_BAR
|
||||
|
||||
s5 := strutil.UpperSnakeCase("Foo-#1😄$_%^&*(1bar")
|
||||
fmt.Println(s5) //FOO_1_1_BAR
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ func main() {
|
||||
|
||||
|
||||
### <span id="ExecCommand">CompareOsEnv</span>
|
||||
<p>Use shell /bin/bash -c(linux) or cmd (windows) to execute command.</p>
|
||||
<p>Execute shell command, return the stdout and stderr string of command, and error if error occur. param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1. In linux, use /bin/bash -c to execute command, In windows, use powershell.exe to execute command.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
@@ -227,10 +227,24 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
out, errout, err := system.ExecCommand("ls")
|
||||
fmt.Println(out)
|
||||
fmt.Println(errout)
|
||||
fmt.Println(err)
|
||||
// linux or mac
|
||||
stdout, stderr, err := system.ExecCommand("ls")
|
||||
fmt.Println("std out: ", stdout)
|
||||
fmt.Println("std err: ", stderr)
|
||||
assert.Equal("", stderr)
|
||||
|
||||
// windows
|
||||
stdout, stderr, err = system.ExecCommand("dir")
|
||||
fmt.Println("std out: ", stdout)
|
||||
fmt.Println("std err: ", stderr)
|
||||
|
||||
// error command
|
||||
stdout, stderr, err = system.ExecCommand("abc")
|
||||
fmt.Println("std out: ", stdout)
|
||||
fmt.Println("std err: ", stderr)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ func main() {
|
||||
|
||||
|
||||
### <span id="ExecCommand">ExecCommand</span>
|
||||
<p>使用shell /bin/bash -c(linux) 或 cmd (windows) 执行shell命令</p>
|
||||
<p>执行shell命令,返回命令的stdout和stderr字符串,如果出现错误,则返回错误。参数`command`是一个完整的命令字符串,如ls-a(linux),dir(windows),ping 127.0.0.1。在linux中,使用/bin/bash-c执行命令,在windows中,使用powershell.exe执行命令。</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
@@ -228,10 +228,24 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
out, errout, err := system.ExecCommand("ls")
|
||||
fmt.Println(out)
|
||||
fmt.Println(errout)
|
||||
fmt.Println(err)
|
||||
// linux or mac
|
||||
stdout, stderr, err := system.ExecCommand("ls")
|
||||
fmt.Println("std out: ", stdout)
|
||||
fmt.Println("std err: ", stderr)
|
||||
assert.Equal("", stderr)
|
||||
|
||||
// windows
|
||||
stdout, stderr, err = system.ExecCommand("dir")
|
||||
fmt.Println("std out: ", stdout)
|
||||
fmt.Println("std err: ", stderr)
|
||||
|
||||
// error command
|
||||
stdout, stderr, err = system.ExecCommand("abc")
|
||||
fmt.Println("std out: ", stdout)
|
||||
fmt.Println("std err: ", stderr)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import (
|
||||
- [IsUrl](#IsUrl)
|
||||
- [IsWeakPassword](#IsWeakPassword)
|
||||
- [IsZeroValue](#IsZeroValue)
|
||||
- [IsGBK](#IsGBK)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
@@ -821,7 +822,34 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="IsGBK">IsGBK</span>
|
||||
<p>Checks if data encoding is gbk(Chinese character internal code extension specification). this function is implemented by whether double bytes fall within the encoding range of gbk,while each byte of utf-8 encoding format falls within the encoding range of gbk.Therefore, utf8.valid() should be called first to check whether it is not utf-8 encoding and then call IsGBK() to check gbk encoding. like the example.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsGBK(data []byte) bool
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
data := []byte("你好")
|
||||
|
||||
// check utf8 first
|
||||
if utf8.Valid(data) {
|
||||
fmt.Println("data encoding is utf-8")
|
||||
}else if(validator.IsGBK(data)) {
|
||||
fmt.Println("data encoding is GBK")
|
||||
}
|
||||
fmt.Println("data encoding is unknown")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import (
|
||||
- [IsUrl](#IsUrl)
|
||||
- [IsWeakPassword](#IsWeakPassword)
|
||||
- [IsZeroValue](#IsZeroValue)
|
||||
- [IsGBK](#IsGBK)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
@@ -820,3 +821,31 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsGBK">IsGBK</span>
|
||||
<p>检查数据编码是否为gbk(汉字内部代码扩展规范)。该函数的实现取决于双字节是否在gbk的编码范围内,而utf-8编码格式的每个字节都在gbk编码范围内。因此,应该首先调用utf8.valid检查它是否是utf-8编码,然后调用IsGBK检查gbk编码。如示例所示。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsGBK(data []byte) bool
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
data := []byte("你好")
|
||||
|
||||
// 先检查utf8编码
|
||||
if utf8.Valid(data) {
|
||||
fmt.Println("data encoding is utf-8")
|
||||
}else if(validator.IsGBK(data)) {
|
||||
fmt.Println("data encoding is GBK")
|
||||
}
|
||||
fmt.Println("data encoding is unknown")
|
||||
}
|
||||
```
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
@@ -78,13 +77,16 @@ func CopyFile(srcFilePath string, dstFilePath string) error {
|
||||
var tmp = make([]byte, 1024*4)
|
||||
for {
|
||||
n, err := srcFile.Read(tmp)
|
||||
distFile.Write(tmp[:n])
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
_, err = distFile.Write(tmp[:n])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +104,7 @@ func ClearFile(path string) error {
|
||||
|
||||
//ReadFileToString return string of file content
|
||||
func ReadFileToString(path string) (string, error) {
|
||||
bytes, err := ioutil.ReadFile(path)
|
||||
bytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -141,7 +143,7 @@ func ListFileNames(path string) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
fs, err := ioutil.ReadDir(path)
|
||||
fs, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
@@ -172,7 +174,7 @@ func Zip(fpath string, destPath string) error {
|
||||
archive := zip.NewWriter(zipFile)
|
||||
defer archive.Close()
|
||||
|
||||
filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error {
|
||||
err = filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -209,6 +211,10 @@ func Zip(fpath string, destPath string) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -229,9 +235,13 @@ func UnZip(zipFile string, destPath string) error {
|
||||
}
|
||||
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(path, os.ModePerm)
|
||||
err = os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
||||
err = os.MkdirAll(filepath.Dir(path), os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,10 @@ func TestCreateFile(t *testing.T) {
|
||||
f := "./text.txt"
|
||||
if CreateFile(f) {
|
||||
file, err := os.Open(f)
|
||||
defer file.Close()
|
||||
assert.IsNil(err)
|
||||
assert.Equal(f, file.Name())
|
||||
|
||||
defer file.Close()
|
||||
} else {
|
||||
t.FailNow()
|
||||
}
|
||||
@@ -113,7 +114,11 @@ func TestReadFileToString(t *testing.T) {
|
||||
|
||||
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
defer f.Close()
|
||||
f.WriteString("hello world")
|
||||
|
||||
_, err := f.WriteString("hello world")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
content, _ := ReadFileToString(path)
|
||||
assert.Equal("hello world", content)
|
||||
@@ -130,9 +135,12 @@ func TestClearFile(t *testing.T) {
|
||||
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
defer f.Close()
|
||||
|
||||
f.WriteString("hello world")
|
||||
_, err := f.WriteString("hello world")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
err := ClearFile(path)
|
||||
err = ClearFile(path)
|
||||
assert.IsNil(err)
|
||||
|
||||
content, _ := ReadFileToString(path)
|
||||
@@ -148,8 +156,13 @@ func TestReadFileByLine(t *testing.T) {
|
||||
CreateFile(path)
|
||||
|
||||
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
|
||||
defer f.Close()
|
||||
f.WriteString("hello\nworld")
|
||||
|
||||
_, err := f.WriteString("hello\nworld")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
expected := []string{"hello", "world"}
|
||||
actual, _ := ReadFileByLine(path)
|
||||
@@ -166,10 +179,14 @@ func TestZipAndUnZip(t *testing.T) {
|
||||
|
||||
file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
defer file.Close()
|
||||
file.WriteString("hello\nworld")
|
||||
|
||||
_, err := file.WriteString("hello\nworld")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
zipFile := "./text.zip"
|
||||
err := Zip(srcFile, zipFile)
|
||||
err = Zip(srcFile, zipFile)
|
||||
assert.IsNil(err)
|
||||
|
||||
unZipPath := "./unzip"
|
||||
|
||||
@@ -4,14 +4,25 @@
|
||||
// Package formatter implements some functions to format string, struct.
|
||||
package formatter
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// Comma add comma to a number value by every 3 numbers from right. ahead by symbol char.
|
||||
// if value is invalid number string eg "aa", return empty string
|
||||
// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345"
|
||||
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string {
|
||||
s, err := numberToString(value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Comma add comma to number by every 3 numbers from right. ahead by symbol char
|
||||
func Comma(v any, symbol string) string {
|
||||
s := numString(v)
|
||||
dotIndex := strings.Index(s, ".")
|
||||
if dotIndex != -1 {
|
||||
return symbol + commaString(s[:dotIndex]) + s[dotIndex:]
|
||||
}
|
||||
|
||||
return symbol + commaString(s)
|
||||
}
|
||||
|
||||
@@ -14,27 +14,34 @@ func commaString(s string) string {
|
||||
return commaString(s[:len(s)-3]) + "," + commaString(s[len(s)-3:])
|
||||
}
|
||||
|
||||
func numString(value any) string {
|
||||
func numberToString(value any) (string, error) {
|
||||
switch reflect.TypeOf(value).Kind() {
|
||||
case reflect.Int, reflect.Int64, reflect.Float32, reflect.Float64:
|
||||
return fmt.Sprintf("%v", value)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return fmt.Sprintf("%v", value), nil
|
||||
|
||||
// todo: need to handle 12345678.9 => 1.23456789e+07
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return fmt.Sprintf("%v", value), nil
|
||||
|
||||
case reflect.String:
|
||||
{
|
||||
sv := fmt.Sprintf("%v", value)
|
||||
if strings.Contains(sv, ".") {
|
||||
_, err := strconv.ParseFloat(sv, 64)
|
||||
if err == nil {
|
||||
return sv
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sv, nil
|
||||
} else {
|
||||
_, err := strconv.ParseInt(sv, 10, 64)
|
||||
if err == nil {
|
||||
return sv
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return sv, nil
|
||||
}
|
||||
}
|
||||
default:
|
||||
return ""
|
||||
return "", nil
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -12,12 +12,16 @@ func TestComma(t *testing.T) {
|
||||
assert.Equal("", Comma("", ""))
|
||||
assert.Equal("", Comma("aa", ""))
|
||||
assert.Equal("", Comma("aa.a", ""))
|
||||
assert.Equal("", Comma([]int{1}, ""))
|
||||
assert.Equal("123", Comma("123", ""))
|
||||
assert.Equal("12,345", Comma("12345", ""))
|
||||
assert.Equal("12,345.6789", Comma("12345.6789", ""))
|
||||
assert.Equal("123,456,789,000", Comma("123456789000", ""))
|
||||
|
||||
assert.Equal("12,345", Comma(12345, ""))
|
||||
assert.Equal("$12,345", Comma(12345, "$"))
|
||||
assert.Equal("¥12,345", Comma(12345, "¥"))
|
||||
assert.Equal("12,345.6789", Comma(12345.6789, ""))
|
||||
assert.Equal("12,345.6789", Comma(+12345.6789, ""))
|
||||
// assert.Equal("12,345,678.9", Comma(12345678.9, ""))
|
||||
assert.Equal("123,456,789,000", Comma(123456789000, ""))
|
||||
}
|
||||
|
||||
@@ -84,10 +84,8 @@ func Debounced(fn func(), duration time.Duration) func() {
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
go fn()
|
||||
}
|
||||
<-timer.C
|
||||
go fn()
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
5
go.mod
5
go.mod
@@ -1,3 +1,8 @@
|
||||
module github.com/duke-git/lancet/v2
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a
|
||||
golang.org/x/text v0.5.0
|
||||
)
|
||||
|
||||
4
go.sum
Normal file
4
go.sum
Normal file
@@ -0,0 +1,4 @@
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
@@ -3,14 +3,16 @@
|
||||
|
||||
// Package iterator provides a way to iterate over values stored in containers.
|
||||
// note:
|
||||
// 1. Full feature iterator is complicated, this pacakge is just a experiment to explore how iterators could work in Go.
|
||||
// 1. Full feature iterator is complicated, this package is just a experiment to explore how iterators could work in Go.
|
||||
// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs.
|
||||
// 3. It is currently under development, unstable, and will not be completed for some time in the future.
|
||||
// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it.
|
||||
// Hope that Go can support iterator in future. see https://github.com/golang/go/discussions/54245 and https://github.com/golang/go/discussions/56413
|
||||
package iterator
|
||||
|
||||
import "github.com/duke-git/lancet/v2/lancetconstraints"
|
||||
import (
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// Iterator supports iterating over a sequence of values of type `E`.
|
||||
type Iterator[T any] interface {
|
||||
@@ -142,7 +144,7 @@ func (iter *sliceIterator[T]) Set(value T) {
|
||||
|
||||
// FromRange creates a iterator which returns the numeric range between start inclusive and end
|
||||
// exclusive by the step size. start should be less than end, step shoud be positive.
|
||||
func FromRange[T lancetconstraints.Number](start, end, step T) Iterator[T] {
|
||||
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) Iterator[T] {
|
||||
if end < start {
|
||||
panic("RangeIterator: start should be before end")
|
||||
} else if step <= 0 {
|
||||
@@ -152,15 +154,12 @@ func FromRange[T lancetconstraints.Number](start, end, step T) Iterator[T] {
|
||||
return &rangeIterator[T]{start: start, end: end, step: step}
|
||||
}
|
||||
|
||||
type rangeIterator[T lancetconstraints.Number] struct {
|
||||
type rangeIterator[T constraints.Integer | constraints.Float] struct {
|
||||
start, end, step T
|
||||
}
|
||||
|
||||
func (iter *rangeIterator[T]) HasNext() bool {
|
||||
if iter.start >= iter.end {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return iter.start < iter.end
|
||||
}
|
||||
|
||||
func (iter *rangeIterator[T]) Next() (T, bool) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
// Package iterator provides a way to iterate over values stored in containers.
|
||||
// note:
|
||||
// 1. Full feature iterator is complicated, this pacakge is just a experiment to explore how iterators could work in Go.
|
||||
// 1. Full feature iterator is complicated, this package is just a experiment to explore how iterators could work in Go.
|
||||
// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs.
|
||||
// 3. It is currently under development, unstable, and will not be completed for some time in the future.
|
||||
// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it.
|
||||
|
||||
@@ -11,13 +11,3 @@ type Comparator interface {
|
||||
// Descending order: should return 1 -> v1 < v2, 0 -> v1 = v2, -1 -> v1 > v2
|
||||
Compare(v1, v2 any) int
|
||||
}
|
||||
|
||||
// Number contains all types of number and uintptr, used for generics constraint
|
||||
type Number interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64
|
||||
}
|
||||
|
||||
// Ordered is a constraint that permits any ordered type: any type that supports the operators < <= >= >
|
||||
type Ordered interface {
|
||||
Number | ~string
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func Intersect[K comparable, V any](maps ...map[K]V) map[K]V {
|
||||
return m
|
||||
}
|
||||
|
||||
reduceMaps := make([]map[K]V, 2, 2)
|
||||
reduceMaps := make([]map[K]V, 2)
|
||||
result = reducer(maps[0], maps[1])
|
||||
|
||||
for i := 2; i < len(maps); i++ {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/duke-git/lancet/v2/lancetconstraints"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// Exponent calculate x^n
|
||||
@@ -94,7 +94,7 @@ func TruncRound(x float64, n int) float64 {
|
||||
}
|
||||
|
||||
// Max return max value of params
|
||||
func Max[T lancetconstraints.Number](numbers ...T) T {
|
||||
func Max[T constraints.Integer | constraints.Float](numbers ...T) T {
|
||||
max := numbers[0]
|
||||
|
||||
for _, v := range numbers {
|
||||
@@ -128,7 +128,7 @@ func MaxBy[T any](slice []T, comparator func(T, T) bool) T {
|
||||
}
|
||||
|
||||
// Min return min value of params
|
||||
func Min[T lancetconstraints.Number](numbers ...T) T {
|
||||
func Min[T constraints.Integer | constraints.Float](numbers ...T) T {
|
||||
min := numbers[0]
|
||||
|
||||
for _, v := range numbers {
|
||||
@@ -161,8 +161,8 @@ func MinBy[T any](slice []T, comparator func(T, T) bool) T {
|
||||
return min
|
||||
}
|
||||
|
||||
// Average return average value of params
|
||||
func Average[T lancetconstraints.Number](numbers ...T) T {
|
||||
// Average return average value of numbers
|
||||
func Average[T constraints.Integer | constraints.Float](numbers ...T) T {
|
||||
var sum T
|
||||
n := T(len(numbers))
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
@@ -108,7 +108,11 @@ func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, err
|
||||
|
||||
client.setTLS(rawUrl)
|
||||
client.setHeader(req, request.Headers)
|
||||
client.setQueryParam(req, rawUrl, request.QueryParams)
|
||||
|
||||
err = client.setQueryParam(req, rawUrl, request.QueryParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if request.FormData != nil {
|
||||
client.setFormData(req, request.FormData)
|
||||
@@ -177,7 +181,7 @@ func (client *HttpClient) setQueryParam(req *http.Request, reqUrl string, queryP
|
||||
|
||||
func (client *HttpClient) setFormData(req *http.Request, values url.Values) {
|
||||
formData := []byte(values.Encode())
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(formData))
|
||||
req.Body = io.NopCloser(bytes.NewReader(formData))
|
||||
req.ContentLength = int64(len(formData))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -32,7 +32,10 @@ func TestHttpClient_Get(t *testing.T) {
|
||||
}
|
||||
|
||||
var todo Todo
|
||||
httpClient.DecodeResponse(resp, &todo)
|
||||
err = httpClient.DecodeResponse(resp, &todo)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
assert.Equal(1, todo.Id)
|
||||
}
|
||||
@@ -58,7 +61,7 @@ func TestHttpClent_Post(t *testing.T) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
@@ -90,6 +93,6 @@ func TestStructToUrlValues(t *testing.T) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", string(body))
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ package netutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
@@ -20,7 +21,7 @@ func TestHttpGet(t *testing.T) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
@@ -40,7 +41,7 @@ func TestHttpPost(t *testing.T) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
@@ -50,23 +51,20 @@ func TestHttpPostFormData(t *testing.T) {
|
||||
// "Content-Type": "application/x-www-form-urlencoded",
|
||||
"Content-Type": "multipart/form-data",
|
||||
}
|
||||
type Todo struct {
|
||||
UserId int `json:"userId"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
// postData := url.Values{}
|
||||
// postData.Add("userId", "1")
|
||||
// postData.Add("title", "TestAddToDo")
|
||||
|
||||
postData := make(map[string]string)
|
||||
postData["userId"] = "1"
|
||||
postData["title"] = "title"
|
||||
postData := url.Values{}
|
||||
postData.Add("userId", "1")
|
||||
postData.Add("title", "TestToDo")
|
||||
|
||||
// postData := make(map[string]string)
|
||||
// postData["userId"] = "1"
|
||||
// postData["title"] = "title"
|
||||
|
||||
resp, err := HttpPost(apiUrl, header, postData, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
@@ -87,7 +85,7 @@ func TestHttpPut(t *testing.T) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
@@ -108,7 +106,7 @@ func TestHttpPatch(t *testing.T) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
@@ -118,7 +116,7 @@ func TestHttpDelete(t *testing.T) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package netutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -55,7 +55,7 @@ func GetPublicIpInfo() (*PublicIpInfo, error) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package netutil
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -173,7 +173,7 @@ func setBodyByte(req *http.Request, body any) error {
|
||||
if body != nil {
|
||||
switch b := body.(type) {
|
||||
case []byte:
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
||||
req.Body = io.NopCloser(bytes.NewReader(b))
|
||||
req.ContentLength = int64(len(b))
|
||||
default:
|
||||
return errors.New("body type should be []byte")
|
||||
|
||||
@@ -11,7 +11,14 @@ import (
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/duke-git/lancet/v2/lancetconstraints"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// Create a static variable to store the hash table.
|
||||
// This variable has the same lifetime as the entire program and can be shared by functions that are called more than once.
|
||||
var (
|
||||
memoryHashMap = make(map[string]map[any]int)
|
||||
memoryHashCounter = make(map[string]int)
|
||||
)
|
||||
|
||||
// Contain check if the target value is in the slice or not
|
||||
@@ -113,7 +120,7 @@ func DifferenceBy[T comparable](slice []T, comparedSlice []T, iteratee func(inde
|
||||
return result
|
||||
}
|
||||
|
||||
//DifferenceWith accepts comparator which is invoked to compare elements of slice to values. The order and references of result values are determined by the first slice. The comparator is invoked with two arguments: (arrVal, othVal).
|
||||
// DifferenceWith accepts comparator which is invoked to compare elements of slice to values. The order and references of result values are determined by the first slice. The comparator is invoked with two arguments: (arrVal, othVal).
|
||||
func DifferenceWith[T any](slice []T, comparedSlice []T, comparator func(item1, item2 T) bool) []T {
|
||||
result := make([]T, 0)
|
||||
|
||||
@@ -729,7 +736,7 @@ func Shuffle[T any](slice []T) []T {
|
||||
|
||||
// Sort sorts a slice of any ordered type(number or string), use quick sort algrithm.
|
||||
// default sort order is ascending (asc), if want descending order, set param `sortOrder` to `desc`
|
||||
func Sort[T lancetconstraints.Ordered](slice []T, sortOrder ...string) {
|
||||
func Sort[T constraints.Ordered](slice []T, sortOrder ...string) {
|
||||
if len(sortOrder) > 0 && sortOrder[0] == "desc" {
|
||||
quickSort(slice, 0, len(slice)-1, "desc")
|
||||
} else {
|
||||
@@ -832,14 +839,44 @@ func Without[T comparable](slice []T, items ...T) []T {
|
||||
return result
|
||||
}
|
||||
|
||||
// IndexOf returns the index at which the first occurrence of a item is found in a slice or return -1 if the item cannot be found.
|
||||
func IndexOf[T comparable](slice []T, item T) int {
|
||||
for i, v := range slice {
|
||||
if v == item {
|
||||
return i
|
||||
// IndexOf returns the index at which the first occurrence of an item is found in a slice or return -1 if the item cannot be found.
|
||||
func IndexOf[T comparable](arr []T, val T) int {
|
||||
limit := 10
|
||||
// gets the hash value of the array as the key of the hash table.
|
||||
key := fmt.Sprintf("%p", arr)
|
||||
// determines whether the hash table is empty. If so, the hash table is created.
|
||||
if memoryHashMap[key] == nil {
|
||||
memoryHashMap[key] = make(map[any]int)
|
||||
// iterate through the array, adding the value and index of each element to the hash table.
|
||||
for i := len(arr) - 1; i >= 0; i-- {
|
||||
memoryHashMap[key][arr[i]] = i
|
||||
}
|
||||
}
|
||||
// update the hash table counter.
|
||||
memoryHashCounter[key]++
|
||||
|
||||
// use the hash table to find the specified value. If found, the index is returned.
|
||||
if index, ok := memoryHashMap[key][val]; ok {
|
||||
// calculate the memory usage of the hash table.
|
||||
size := len(memoryHashMap)
|
||||
// If the memory usage of the hash table exceeds the memory limit, the hash table with the lowest counter is cleared.
|
||||
if size > limit {
|
||||
var minKey string
|
||||
var minVal int
|
||||
for k, v := range memoryHashCounter {
|
||||
if k == key {
|
||||
continue
|
||||
}
|
||||
if minVal == 0 || v < minVal {
|
||||
minKey = k
|
||||
minVal = v
|
||||
}
|
||||
}
|
||||
delete(memoryHashMap, minKey)
|
||||
delete(memoryHashCounter, minKey)
|
||||
}
|
||||
return index
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/duke-git/lancet/v2/lancetconstraints"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// sliceValue return the reflect value of a slice
|
||||
@@ -27,7 +27,7 @@ func sliceElemType(reflectType reflect.Type) reflect.Type {
|
||||
}
|
||||
}
|
||||
|
||||
func quickSort[T lancetconstraints.Ordered](slice []T, lowIndex, highIndex int, order string) {
|
||||
func quickSort[T constraints.Ordered](slice []T, lowIndex, highIndex int, order string) {
|
||||
if lowIndex < highIndex {
|
||||
p := partitionOrderedSlice(slice, lowIndex, highIndex, order)
|
||||
quickSort(slice, lowIndex, p-1, order)
|
||||
@@ -36,7 +36,7 @@ func quickSort[T lancetconstraints.Ordered](slice []T, lowIndex, highIndex int,
|
||||
}
|
||||
|
||||
// partitionOrderedSlice split ordered slice into two parts for quick sort
|
||||
func partitionOrderedSlice[T lancetconstraints.Ordered](slice []T, lowIndex, highIndex int, order string) int {
|
||||
func partitionOrderedSlice[T constraints.Ordered](slice []T, lowIndex, highIndex int, order string) int {
|
||||
p := slice[highIndex]
|
||||
i := lowIndex
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package slice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
@@ -669,8 +670,39 @@ func TestIndexOf(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIndexOf")
|
||||
|
||||
arr := []string{"a", "a", "b", "c"}
|
||||
key := fmt.Sprintf("%p", arr)
|
||||
assert.Equal(0, IndexOf(arr, "a"))
|
||||
assert.Equal(-1, IndexOf(arr, "d"))
|
||||
assert.Equal(2, memoryHashCounter[key])
|
||||
|
||||
arr1 := []int{1, 2, 3, 4, 5}
|
||||
key1 := fmt.Sprintf("%p", arr1)
|
||||
assert.Equal(3, IndexOf(arr1, 4))
|
||||
assert.Equal(-1, IndexOf(arr1, 6))
|
||||
assert.Equal(2, memoryHashCounter[key1])
|
||||
|
||||
arr2 := []float64{1.1, 2.2, 3.3, 4.4, 5.5}
|
||||
key2 := fmt.Sprintf("%p", arr2)
|
||||
assert.Equal(2, IndexOf(arr2, 3.3))
|
||||
assert.Equal(3, IndexOf(arr2, 4.4))
|
||||
assert.Equal(-1, IndexOf(arr2, 6.6))
|
||||
assert.Equal(3, memoryHashCounter[key2])
|
||||
|
||||
for i := 0; i < 6; i++ {
|
||||
a := []string{"a", "b", "c"}
|
||||
IndexOf(a, "a")
|
||||
IndexOf(a, "b")
|
||||
}
|
||||
minArr := []string{"c", "b", "a"}
|
||||
minKey := fmt.Sprintf("%p", minArr)
|
||||
assert.Equal(0, IndexOf(minArr, "c"))
|
||||
|
||||
arr3 := []string{"q", "w", "e"}
|
||||
key3 := fmt.Sprintf("%p", arr3)
|
||||
assert.Equal(1, IndexOf(arr3, "w"))
|
||||
assert.Equal(-1, IndexOf(arr3, "r"))
|
||||
assert.Equal(2, memoryHashCounter[key3])
|
||||
assert.Equal(0, memoryHashCounter[minKey])
|
||||
}
|
||||
|
||||
func TestLastIndexOf(t *testing.T) {
|
||||
|
||||
@@ -4,53 +4,41 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// CamelCase covert string to camelCase string.
|
||||
// non letters and numbers will be ignored
|
||||
// eg. "Foo-#1😄$_%^&*(1bar" => "foo11Bar"
|
||||
func CamelCase(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
var builder strings.Builder
|
||||
|
||||
result := ""
|
||||
blankSpace := " "
|
||||
regex, _ := regexp.Compile("[-_&]+")
|
||||
ss := regex.ReplaceAllString(s, blankSpace)
|
||||
for i, v := range strings.Split(ss, blankSpace) {
|
||||
vv := []rune(v)
|
||||
strs := splitIntoStrings(s, false)
|
||||
for i, str := range strs {
|
||||
if i == 0 {
|
||||
if vv[i] >= 65 && vv[i] <= 96 {
|
||||
vv[0] += 32
|
||||
}
|
||||
result += string(vv)
|
||||
builder.WriteString(strings.ToLower(str))
|
||||
} else {
|
||||
result += Capitalize(v)
|
||||
builder.WriteString(Capitalize(str))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// Capitalize converts the first character of a string to upper case and the remaining to lower case.
|
||||
func Capitalize(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
out := make([]rune, len(s))
|
||||
result := make([]rune, len(s))
|
||||
for i, v := range s {
|
||||
if i == 0 {
|
||||
out[i] = unicode.ToUpper(v)
|
||||
result[i] = unicode.ToUpper(v)
|
||||
} else {
|
||||
out[i] = unicode.ToLower(v)
|
||||
result[i] = unicode.ToLower(v)
|
||||
}
|
||||
}
|
||||
|
||||
return string(out)
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// UpperFirst converts the first character of string to upper case.
|
||||
@@ -116,46 +104,34 @@ func PadStart(source string, size int, padStr string) string {
|
||||
}
|
||||
|
||||
// KebabCase covert string to kebab-case
|
||||
// non letters and numbers will be ignored
|
||||
// eg. "Foo-#1😄$_%^&*(1bar" => "foo-1-1-bar"
|
||||
func KebabCase(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
regex := regexp.MustCompile(`[\W|_]+`)
|
||||
blankSpace := " "
|
||||
match := regex.ReplaceAllString(s, blankSpace)
|
||||
rs := strings.Split(match, blankSpace)
|
||||
|
||||
var result []string
|
||||
for _, v := range rs {
|
||||
splitWords := splitWordsToLower(v)
|
||||
if len(splitWords) > 0 {
|
||||
result = append(result, splitWords...)
|
||||
}
|
||||
}
|
||||
result := splitIntoStrings(s, false)
|
||||
return strings.Join(result, "-")
|
||||
}
|
||||
|
||||
// UpperKebabCase covert string to upper KEBAB-CASE
|
||||
// non letters and numbers will be ignored
|
||||
// eg. "Foo-#1😄$_%^&*(1bar" => "FOO-1-1-BAR"
|
||||
func UpperKebabCase(s string) string {
|
||||
result := splitIntoStrings(s, true)
|
||||
return strings.Join(result, "-")
|
||||
}
|
||||
|
||||
// SnakeCase covert string to snake_case
|
||||
// non letters and numbers will be ignored
|
||||
// eg. "Foo-#1😄$_%^&*(1bar" => "foo_1_1_bar"
|
||||
func SnakeCase(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
regex := regexp.MustCompile(`[\W|_]+`)
|
||||
blankSpace := " "
|
||||
match := regex.ReplaceAllString(s, blankSpace)
|
||||
rs := strings.Split(match, blankSpace)
|
||||
|
||||
var result []string
|
||||
for _, v := range rs {
|
||||
splitWords := splitWordsToLower(v)
|
||||
if len(splitWords) > 0 {
|
||||
result = append(result, splitWords...)
|
||||
}
|
||||
}
|
||||
result := splitIntoStrings(s, false)
|
||||
return strings.Join(result, "_")
|
||||
}
|
||||
|
||||
// UpperSnakeCase covert string to upper SNAKE_CASE
|
||||
// non letters and numbers will be ignored
|
||||
// eg. "Foo-#1😄$_%^&*(1bar" => "FOO_1_1_BAR"
|
||||
func UpperSnakeCase(s string) string {
|
||||
result := splitIntoStrings(s, true)
|
||||
return strings.Join(result, "_")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +1,100 @@
|
||||
package strutil
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// splitWordsToLower split a string into worlds by uppercase char
|
||||
func splitWordsToLower(s string) []string {
|
||||
var result []string
|
||||
func splitIntoStrings(s string, upperCase bool) []string {
|
||||
var runes [][]rune
|
||||
lastCharType := 0
|
||||
charType := 0
|
||||
|
||||
upperIndexes := upperIndex(s)
|
||||
l := len(upperIndexes)
|
||||
if upperIndexes == nil || l == 0 {
|
||||
if s != "" {
|
||||
result = append(result, s)
|
||||
// split into fields based on type of unicode character
|
||||
for _, r := range s {
|
||||
switch true {
|
||||
case isLower(r):
|
||||
charType = 1
|
||||
case isUpper(r):
|
||||
charType = 2
|
||||
case isDigit(r):
|
||||
charType = 3
|
||||
default:
|
||||
charType = 4
|
||||
}
|
||||
return result
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
if i < l-1 {
|
||||
result = append(result, strings.ToLower(s[upperIndexes[i]:upperIndexes[i+1]]))
|
||||
|
||||
if charType == lastCharType {
|
||||
runes[len(runes)-1] = append(runes[len(runes)-1], r)
|
||||
} else {
|
||||
result = append(result, strings.ToLower(s[upperIndexes[i]:]))
|
||||
runes = append(runes, []rune{r})
|
||||
}
|
||||
lastCharType = charType
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// upperIndex get a int slice which elements are all the uppercase char index of a string
|
||||
func upperIndex(s string) []int {
|
||||
var result []int
|
||||
for i := 0; i < len(s); i++ {
|
||||
if 64 < s[i] && s[i] < 91 {
|
||||
result = append(result, i)
|
||||
for i := 0; i < len(runes)-1; i++ {
|
||||
if isUpper(runes[i][0]) && isLower(runes[i+1][0]) {
|
||||
runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
|
||||
runes[i] = runes[i][:len(runes[i])-1]
|
||||
}
|
||||
}
|
||||
if len(s) > 0 && result != nil && result[0] != 0 {
|
||||
result = append([]int{0}, result...)
|
||||
|
||||
// filter all none letters and none digit
|
||||
var result []string
|
||||
for _, rs := range runes {
|
||||
if len(rs) > 0 && (unicode.IsLetter(rs[0]) || isDigit(rs[0])) {
|
||||
if upperCase {
|
||||
result = append(result, string(toUpperAll(rs)))
|
||||
} else {
|
||||
result = append(result, string(toLowerAll(rs)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// isDigit checks if a character is digit ('0' to '9')
|
||||
func isDigit(r rune) bool {
|
||||
return r >= '0' && r <= '9'
|
||||
}
|
||||
|
||||
// isLower checks if a character is lower case ('a' to 'z')
|
||||
func isLower(r rune) bool {
|
||||
return r >= 'a' && r <= 'z'
|
||||
}
|
||||
|
||||
// isUpper checks if a character is upper case ('A' to 'Z')
|
||||
func isUpper(r rune) bool {
|
||||
return r >= 'A' && r <= 'Z'
|
||||
}
|
||||
|
||||
// toLower converts a character 'A' to 'Z' to its lower case
|
||||
func toLower(r rune) rune {
|
||||
if r >= 'A' && r <= 'Z' {
|
||||
return r + 32
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// toLowerAll converts a character 'A' to 'Z' to its lower case
|
||||
func toLowerAll(rs []rune) []rune {
|
||||
for i := range rs {
|
||||
rs[i] = toLower(rs[i])
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
// toUpper converts a character 'a' to 'z' to its upper case
|
||||
func toUpper(r rune) rune {
|
||||
if r >= 'a' && r <= 'z' {
|
||||
return r - 32
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// toUpperAll converts a character 'a' to 'z' to its upper case
|
||||
func toUpperAll(rs []rune) []rune {
|
||||
for i := range rs {
|
||||
rs[i] = toUpper(rs[i])
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
@@ -9,67 +9,163 @@ import (
|
||||
func TestCamelCase(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCamelCase")
|
||||
|
||||
assert.Equal("fooBar", CamelCase("foo_bar"))
|
||||
assert.Equal("fooBar", CamelCase("Foo-Bar"))
|
||||
assert.Equal("fooBar", CamelCase("Foo&bar"))
|
||||
assert.Equal("fooBar", CamelCase("foo bar"))
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"foobar": "foobar",
|
||||
"&FOO:BAR$BAZ": "fooBarBaz",
|
||||
"fooBar": "fooBar",
|
||||
"FOObar": "foObar",
|
||||
"$foo%": "foo",
|
||||
" $#$Foo 22 bar ": "foo22Bar",
|
||||
"Foo-#1😄$_%^&*(1bar": "foo11Bar",
|
||||
}
|
||||
|
||||
assert.NotEqual("FooBar", CamelCase("foo_bar"))
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, CamelCase(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCapitalize(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCapitalize")
|
||||
|
||||
assert.Equal("Foo", Capitalize("foo"))
|
||||
assert.Equal("Foo", Capitalize("Foo"))
|
||||
assert.Equal("Foo", Capitalize("Foo"))
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"Foo": "Foo",
|
||||
"_foo": "_foo",
|
||||
"foobar": "Foobar",
|
||||
"fooBar": "Foobar",
|
||||
"foo Bar": "Foo bar",
|
||||
"foo-bar": "Foo-bar",
|
||||
"$foo%": "$foo%",
|
||||
}
|
||||
|
||||
assert.NotEqual("foo", Capitalize("Foo"))
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, Capitalize(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestKebabCase(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestKebabCase")
|
||||
|
||||
assert.Equal("foo-bar", KebabCase("Foo Bar-"))
|
||||
assert.Equal("foo-bar", KebabCase("foo_Bar"))
|
||||
assert.Equal("foo-bar", KebabCase("fooBar"))
|
||||
assert.Equal("f-o-o-b-a-r", KebabCase("__FOO_BAR__"))
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"foo-bar": "foo-bar",
|
||||
"--Foo---Bar-": "foo-bar",
|
||||
"Foo Bar-": "foo-bar",
|
||||
"foo_Bar": "foo-bar",
|
||||
"fooBar": "foo-bar",
|
||||
"FOOBAR": "foobar",
|
||||
"FOO_BAR": "foo-bar",
|
||||
"__FOO_BAR__": "foo-bar",
|
||||
"$foo@Bar": "foo-bar",
|
||||
" $#$Foo 22 bar ": "foo-22-bar",
|
||||
"Foo-#1😄$_%^&*(1bar": "foo-1-1-bar",
|
||||
}
|
||||
|
||||
assert.NotEqual("foo_bar", KebabCase("fooBar"))
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, KebabCase(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpperKebabCase(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestUpperKebabCase")
|
||||
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"foo-bar": "FOO-BAR",
|
||||
"--Foo---Bar-": "FOO-BAR",
|
||||
"Foo Bar-": "FOO-BAR",
|
||||
"foo_Bar": "FOO-BAR",
|
||||
"fooBar": "FOO-BAR",
|
||||
"FOOBAR": "FOOBAR",
|
||||
"FOO_BAR": "FOO-BAR",
|
||||
"__FOO_BAR__": "FOO-BAR",
|
||||
"$foo@Bar": "FOO-BAR",
|
||||
" $#$Foo 22 bar ": "FOO-22-BAR",
|
||||
"Foo-#1😄$_%^&*(1bar": "FOO-1-1-BAR",
|
||||
}
|
||||
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, UpperKebabCase(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnakeCase(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSnakeCase")
|
||||
|
||||
assert.Equal("foo_bar", SnakeCase("Foo Bar-"))
|
||||
assert.Equal("foo_bar", SnakeCase("foo_Bar"))
|
||||
assert.Equal("foo_bar", SnakeCase("fooBar"))
|
||||
assert.Equal("f_o_o_b_a_r", SnakeCase("__FOO_BAR__"))
|
||||
assert.Equal("a_bbc_s_a_b_b_c", SnakeCase("aBbc-s$@a&%_B.B^C"))
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"foo-bar": "foo_bar",
|
||||
"--Foo---Bar-": "foo_bar",
|
||||
"Foo Bar-": "foo_bar",
|
||||
"foo_Bar": "foo_bar",
|
||||
"fooBar": "foo_bar",
|
||||
"FOOBAR": "foobar",
|
||||
"FOO_BAR": "foo_bar",
|
||||
"__FOO_BAR__": "foo_bar",
|
||||
"$foo@Bar": "foo_bar",
|
||||
" $#$Foo 22 bar ": "foo_22_bar",
|
||||
"Foo-#1😄$_%^&*(1bar": "foo_1_1_bar",
|
||||
}
|
||||
|
||||
assert.NotEqual("foo-bar", SnakeCase("foo_Bar"))
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, SnakeCase(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpperSnakeCase(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestUpperSnakeCase")
|
||||
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"foo-bar": "FOO_BAR",
|
||||
"--Foo---Bar-": "FOO_BAR",
|
||||
"Foo Bar-": "FOO_BAR",
|
||||
"foo_Bar": "FOO_BAR",
|
||||
"fooBar": "FOO_BAR",
|
||||
"FOOBAR": "FOOBAR",
|
||||
"FOO_BAR": "FOO_BAR",
|
||||
"__FOO_BAR__": "FOO_BAR",
|
||||
"$foo@Bar": "FOO_BAR",
|
||||
" $#$Foo 22 bar ": "FOO_22_BAR",
|
||||
"Foo-#1😄$_%^&*(1bar": "FOO_1_1_BAR",
|
||||
}
|
||||
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, UpperSnakeCase(k))
|
||||
}
|
||||
}
|
||||
|
||||
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大"))
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"foo": "Foo",
|
||||
"bAR": "BAR",
|
||||
"FOo": "FOo",
|
||||
"fOo大": "FOo大",
|
||||
}
|
||||
|
||||
assert.NotEqual("Bar", UpperFirst("BAR"))
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, UpperFirst(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLowerFirst(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestLowerFirst")
|
||||
|
||||
assert.Equal("foo", LowerFirst("foo"))
|
||||
assert.Equal("bAR", LowerFirst("BAR"))
|
||||
assert.Equal("fOo", LowerFirst("FOo"))
|
||||
assert.Equal("fOo大", LowerFirst("FOo大"))
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"foo": "foo",
|
||||
"bAR": "bAR",
|
||||
"FOo": "fOo",
|
||||
"fOo大": "fOo大",
|
||||
}
|
||||
|
||||
assert.NotEqual("Bar", LowerFirst("BAR"))
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, LowerFirst(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPadEnd(t *testing.T) {
|
||||
|
||||
50
system/os.go
50
system/os.go
@@ -9,6 +9,10 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
)
|
||||
|
||||
// IsWindows check if current os is windows
|
||||
@@ -50,27 +54,61 @@ func CompareOsEnv(key, comparedEnv string) bool {
|
||||
return env == comparedEnv
|
||||
}
|
||||
|
||||
// ExecCommand use shell /bin/bash -c to execute command
|
||||
// ExecCommand execute command, return the stdout and stderr string of command, and error if error occur
|
||||
// param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1
|
||||
// in linux, use /bin/bash -c to execute command
|
||||
// in windows, use powershell.exe to execute command
|
||||
func ExecCommand(command string) (stdout, stderr string, err error) {
|
||||
var out bytes.Buffer
|
||||
var errout bytes.Buffer
|
||||
var errOut bytes.Buffer
|
||||
|
||||
cmd := exec.Command("/bin/bash", "-c", command)
|
||||
if IsWindows() {
|
||||
cmd = exec.Command("cmd")
|
||||
cmd = exec.Command("powershell.exe", command)
|
||||
}
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &errout
|
||||
cmd.Stderr = &errOut
|
||||
|
||||
err = cmd.Run()
|
||||
|
||||
if err != nil {
|
||||
stderr = string(errout.Bytes())
|
||||
if utf8.Valid(errOut.Bytes()) {
|
||||
stderr = byteToString(errOut.Bytes(), "UTF8")
|
||||
} else if validator.IsGBK(errOut.Bytes()) {
|
||||
stderr = byteToString(errOut.Bytes(), "GBK")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data := out.Bytes()
|
||||
if utf8.Valid(data) {
|
||||
stdout = byteToString(data, "UTF8")
|
||||
} else if validator.IsGBK(data) {
|
||||
stdout = byteToString(data, "GBK")
|
||||
}
|
||||
stdout = string(out.Bytes())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func byteToString(data []byte, charset string) string {
|
||||
var result string
|
||||
|
||||
switch charset {
|
||||
case "GBK":
|
||||
decodeBytes, _ := simplifiedchinese.GBK.NewDecoder().Bytes(data)
|
||||
result = string(decodeBytes)
|
||||
case "GB18030":
|
||||
decodeBytes, _ := simplifiedchinese.GB18030.NewDecoder().Bytes(data)
|
||||
result = string(decodeBytes)
|
||||
case "UTF8":
|
||||
fallthrough
|
||||
default:
|
||||
result = string(data)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetOsBits get this system bits 32bit or 64bit
|
||||
// return bit int (32/64)
|
||||
func GetOsBits() int {
|
||||
|
||||
@@ -11,10 +11,10 @@ func TestOsDetection(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestOsJudgment")
|
||||
|
||||
osType, _, _ := ExecCommand("echo $OSTYPE")
|
||||
if strings.Index(osType, "linux") != -1 {
|
||||
if strings.Contains(osType, "linux") {
|
||||
assert.Equal(true, IsLinux())
|
||||
}
|
||||
if strings.Index(osType, "darwin") != -1 {
|
||||
if strings.Contains(osType, "darwin") {
|
||||
assert.Equal(true, IsMac())
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,9 @@ func TestOsEnvOperation(t *testing.T) {
|
||||
envNotExist := GetOsEnv("foo")
|
||||
assert.Equal("", envNotExist)
|
||||
|
||||
SetOsEnv("foo", "foo_value")
|
||||
err := SetOsEnv("foo", "foo_value")
|
||||
assert.IsNil(err)
|
||||
|
||||
envExist := GetOsEnv("foo")
|
||||
assert.Equal("foo_value", envExist)
|
||||
|
||||
@@ -34,7 +36,7 @@ func TestOsEnvOperation(t *testing.T) {
|
||||
assert.Equal(false, CompareOsEnv("abc", "abc"))
|
||||
assert.Equal(false, CompareOsEnv("abc", "abc"))
|
||||
|
||||
err := RemoveOsEnv("foo")
|
||||
err = RemoveOsEnv("foo")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
@@ -44,21 +46,26 @@ func TestOsEnvOperation(t *testing.T) {
|
||||
func TestExecCommand(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestExecCommand")
|
||||
|
||||
out, errout, err := ExecCommand("ls")
|
||||
t.Log("std out: ", out)
|
||||
t.Log("std err: ", errout)
|
||||
// linux or mac
|
||||
stdout, stderr, err := ExecCommand("ls")
|
||||
t.Log("std out: ", stdout)
|
||||
t.Log("std err: ", stderr)
|
||||
assert.Equal("", stderr)
|
||||
assert.IsNil(err)
|
||||
|
||||
out, errout, err = ExecCommand("abc")
|
||||
t.Log("std out: ", out)
|
||||
t.Log("std err: ", errout)
|
||||
if err != nil {
|
||||
t.Logf("error: %v\n", err)
|
||||
// windows
|
||||
stdout, stderr, err = ExecCommand("dir")
|
||||
t.Log("std out: ", stdout)
|
||||
t.Log("std err: ", stderr)
|
||||
if IsWindows() {
|
||||
assert.IsNil(err)
|
||||
}
|
||||
|
||||
if !IsWindows() {
|
||||
assert.IsNotNil(err)
|
||||
}
|
||||
// error command
|
||||
stdout, stderr, err = ExecCommand("abc")
|
||||
t.Log("std out: ", stdout)
|
||||
t.Log("std err: ", stderr)
|
||||
assert.IsNotNil(err)
|
||||
}
|
||||
|
||||
func TestGetOsBits(t *testing.T) {
|
||||
|
||||
@@ -15,11 +15,24 @@ import (
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var isAlphaRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
|
||||
var (
|
||||
alphaMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
|
||||
letterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
|
||||
intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
|
||||
urlMatcher *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]*)?$`)
|
||||
dnsMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`)
|
||||
emailMatcher *regexp.Regexp = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`)
|
||||
chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`)
|
||||
chineseIdMatcher *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]$`)
|
||||
chineseMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
|
||||
chinesePhoneMatcher *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}`)
|
||||
creditCardMatcher *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})$`)
|
||||
base64Matcher *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`)
|
||||
)
|
||||
|
||||
// IsAlpha checks if the string contains only letters (a-zA-Z)
|
||||
func IsAlpha(str string) bool {
|
||||
return isAlphaRegexMatcher.MatchString(str)
|
||||
return alphaMatcher.MatchString(str)
|
||||
}
|
||||
|
||||
// IsAllUpper check if the string is all upper case letters A-Z
|
||||
@@ -62,11 +75,9 @@ func ContainLower(str string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var containLetterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
|
||||
|
||||
// ContainLetter check if the string contain at least one letter
|
||||
func ContainLetter(str string) bool {
|
||||
return containLetterRegexMatcher.MatchString(str)
|
||||
return letterRegexMatcher.MatchString(str)
|
||||
}
|
||||
|
||||
// IsJSON checks if the string is valid JSON
|
||||
@@ -86,11 +97,9 @@ func IsFloatStr(str string) bool {
|
||||
return e == nil
|
||||
}
|
||||
|
||||
var isIntStrRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
|
||||
|
||||
// IsIntStr check if the string can convert to a integer.
|
||||
func IsIntStr(str string) bool {
|
||||
return isIntStrRegexMatcher.MatchString(str)
|
||||
return intStrMatcher.MatchString(str)
|
||||
}
|
||||
|
||||
// IsIp check if the string is a ip address.
|
||||
@@ -125,8 +134,6 @@ 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, ".") {
|
||||
@@ -143,64 +150,48 @@ func IsUrl(str string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return isUrlRegexMatcher.MatchString(str)
|
||||
return urlMatcher.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.
|
||||
func IsDns(dns string) bool {
|
||||
return isDnsRegexMatcher.MatchString(dns)
|
||||
return dnsMatcher.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 {
|
||||
return isEmailRegexMatcher.MatchString(email)
|
||||
return emailMatcher.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 {
|
||||
return isChineseMobileRegexMatcher.MatchString(mobileNum)
|
||||
return chineseMobileMatcher.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 {
|
||||
return isChineseIdNumRegexMatcher.MatchString(id)
|
||||
return chineseIdMatcher.MatchString(id)
|
||||
}
|
||||
|
||||
var containChineseRegexMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
|
||||
|
||||
// ContainChinese check if the string contain mandarin chinese.
|
||||
func ContainChinese(s string) bool {
|
||||
return containChineseRegexMatcher.MatchString(s)
|
||||
return chineseMatcher.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 {
|
||||
return isChinesePhoneRegexMatcher.MatchString(phone)
|
||||
return chinesePhoneMatcher.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 {
|
||||
return isCreditCardRegexMatcher.MatchString(creditCart)
|
||||
return creditCardMatcher.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 {
|
||||
return isBase64RegexMatcher.MatchString(base64)
|
||||
return base64Matcher.MatchString(base64)
|
||||
}
|
||||
|
||||
// IsEmptyString check if the string is empty.
|
||||
@@ -283,3 +274,40 @@ func IsZeroValue(value any) bool {
|
||||
|
||||
return reflect.DeepEqual(rv.Interface(), reflect.Zero(rv.Type()).Interface())
|
||||
}
|
||||
|
||||
// IsGBK check if data encoding is gbk
|
||||
// Note: this function is implemented by whether double bytes fall within the encoding range of gbk,
|
||||
// while each byte of utf-8 encoding format falls within the encoding range of gbk.
|
||||
// Therefore, utf8.valid() should be called first to check whether it is not utf-8 encoding,
|
||||
// and then call IsGBK() to check gbk encoding. like below
|
||||
/**
|
||||
data := []byte("你好")
|
||||
if utf8.Valid(data) {
|
||||
fmt.Println("data encoding is utf-8")
|
||||
}else if(IsGBK(data)) {
|
||||
fmt.Println("data encoding is GBK")
|
||||
}
|
||||
fmt.Println("data encoding is unknown")
|
||||
**/
|
||||
func IsGBK(data []byte) bool {
|
||||
i := 0
|
||||
for i < len(data) {
|
||||
if data[i] <= 0xff {
|
||||
i++
|
||||
continue
|
||||
} else {
|
||||
if data[i] >= 0x81 &&
|
||||
data[i] <= 0xfe &&
|
||||
data[i+1] >= 0x40 &&
|
||||
data[i+1] <= 0xfe &&
|
||||
data[i+1] != 0xf7 {
|
||||
i += 2
|
||||
continue
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
)
|
||||
|
||||
func TestIsAllUpper(t *testing.T) {
|
||||
@@ -388,3 +390,13 @@ func TestIsZeroValue(t *testing.T) {
|
||||
assert.Equal(false, IsZeroValue(value))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsGBK(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsGBK")
|
||||
|
||||
str := "你好"
|
||||
gbkData, _ := simplifiedchinese.GBK.NewEncoder().Bytes([]byte(str))
|
||||
|
||||
assert.Equal(true, IsGBK(gbkData))
|
||||
assert.Equal(false, utf8.Valid(gbkData))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user