1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-20 04:22:25 +08:00

Compare commits

...

14 Commits

Author SHA1 Message Date
dudaodong
b7e5d946f1 release v2.1.12 2022-12-15 16:55:20 +08:00
dudaodong
687db4ce79 update readme file 2022-12-15 16:44:30 +08:00
dudaodong
a9a4bb8841 update readme file 2022-12-15 16:42:22 +08:00
dudaodong
bc6cb5f61b fix code issue in doc 2022-12-15 16:38:50 +08:00
dudaodong
2c57266f8e test: update some test functions 2022-12-15 15:22:01 +08:00
dudaodong
57e49c9520 doc: update document for funcations CamelCase/KebabCase/UpperKebabCase/SnakeCase/UpperSnakeCase 2022-12-14 21:42:43 +08:00
dudaodong
985c9a5d9a refactor: refact CamelCase function 2022-12-14 21:27:29 +08:00
dudaodong
5cfb11f036 feat: fix and add SnakeCase/UpperSnakeCase 2022-12-14 21:17:30 +08:00
dudaodong
5b3d48a1e7 feat: fix and add KebabCase/UpperKebabCase 2022-12-14 21:09:22 +08:00
dudaodong
d0576e028f clean code 2022-12-13 19:52:37 +08:00
dudaodong
76bdec2b54 test: add cases for Capitalize 2022-12-13 16:16:16 +08:00
dudaodong
fa20aba3a7 fix: fix CamelCase function bug 2022-12-13 14:23:32 +08:00
dudaodong
7a4a429e23 test: add cases for Capitalize 2022-12-12 21:11:19 +08:00
dudaodong
a70ec6ad1e refactor: use constraints from golang.org/x/exp/constraints 2022-12-11 11:25:34 +08:00
17 changed files with 469 additions and 166 deletions

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.1.11-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.1.12-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -38,10 +38,10 @@ English | [简体中文](./README_zh-CN.md)
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.

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.1.11-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.1.12-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -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 包实现一些错误处理函数

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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
}
```

View File

@@ -22,6 +22,6 @@ func TestComma(t *testing.T) {
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("12,345,678.9", Comma(12345678.9, ""))
assert.Equal("123,456,789,000", Comma(123456789000, ""))
}

View File

@@ -10,7 +10,9 @@
// 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,7 +154,7 @@ 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
}

View File

@@ -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
}

View File

@@ -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))

View File

@@ -11,7 +11,7 @@ import (
"reflect"
"sort"
"github.com/duke-git/lancet/v2/lancetconstraints"
"golang.org/x/exp/constraints"
)
// Create a static variable to store the hash table.
@@ -120,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)
@@ -736,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 {

View File

@@ -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

View File

@@ -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, "_")
}

View File

@@ -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
}

View File

@@ -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) {