mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-20 20:32:29 +08:00
Compare commits
25 Commits
v2.1.9
...
d66f92cd68
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d66f92cd68 | ||
|
|
d8ed692651 | ||
|
|
a16de97d1d | ||
|
|
6f458e4367 | ||
|
|
37c7508ad0 | ||
|
|
acb5844b15 | ||
|
|
76f4eeea16 | ||
|
|
5466a23019 | ||
|
|
5692982dd1 | ||
|
|
c39c8914fb | ||
|
|
29bdca1bd2 | ||
|
|
eb66d038ac | ||
|
|
a99ada5ee1 | ||
|
|
a87faf5453 | ||
|
|
ab6fec2f69 | ||
|
|
7b290989f5 | ||
|
|
5722c724e6 | ||
|
|
f84584ca04 | ||
|
|
80cbbdc787 | ||
|
|
be148e07ba | ||
|
|
d36ab5cc3a | ||
|
|
2b17329094 | ||
|
|
f869a0a670 | ||
|
|
be000a4bd6 | ||
|
|
81efa800ea |
@@ -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)
|
||||
English | [简体中文](./README_zh-CN.md) | [Website](https://uvdream.github.io/lancet-docs/en/)
|
||||
|
||||
## Feature
|
||||
|
||||
@@ -38,7 +38,7 @@ 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.3. </b>
|
||||
2. <b>For users who use version below go1.18, you should install v1.x.x. now latest v1 is v1.3.4. </b>
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet@v1.3.3 // below go1.18, install latest version of v1.x.x
|
||||
|
||||
@@ -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)
|
||||
简体中文 | [English](./README.md) | [文档](https://uvdream.github.io/lancet-docs)
|
||||
|
||||
## 特性
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
|
||||
```
|
||||
|
||||
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.3.3。</b>
|
||||
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.3.4。</b>
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet@v1.3.3 // 使用go1.18以下版本, 必须安装v1.x.x版本
|
||||
|
||||
15
SECURITY.md
Normal file
15
SECURITY.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
Here is the lancet version and compatibility with go language version.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------|
|
||||
| 2.x.x | +go v1.18 |
|
||||
| 1.x.x | +go v1.12 |
|
||||
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
For now, there is no public website to report a vulnerability, If you find security issue in lancet, you can send it to me via my email `lanliddd.2007@163.com`.
|
||||
we can discuss it. I am appreciate if someone can create a public page for reporting vulnerability.
|
||||
@@ -90,34 +90,50 @@ func ToChannel[T any](array []T) <-chan T {
|
||||
}
|
||||
|
||||
// ToString convert value to string
|
||||
// for number, string, []byte, will convert to string
|
||||
// for other type (slice, map, array, struct) will call json.Marshal
|
||||
func ToString(value any) string {
|
||||
result := ""
|
||||
if value == nil {
|
||||
return result
|
||||
return ""
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
switch value.(type) {
|
||||
case float32, float64:
|
||||
result = strconv.FormatFloat(v.Float(), 'f', -1, 64)
|
||||
return result
|
||||
case int, int8, int16, int32, int64:
|
||||
result = strconv.FormatInt(v.Int(), 10)
|
||||
return result
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
result = strconv.FormatUint(v.Uint(), 10)
|
||||
return result
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(value.(float32)), 'f', -1, 32)
|
||||
case float64:
|
||||
return strconv.FormatFloat(value.(float64), 'f', -1, 64)
|
||||
case int:
|
||||
return strconv.FormatInt(int64(value.(int)), 10)
|
||||
case int8:
|
||||
return strconv.FormatInt(int64(value.(int8)), 10)
|
||||
case int16:
|
||||
return strconv.FormatInt(int64(value.(int16)), 10)
|
||||
case int32:
|
||||
return strconv.FormatInt(int64(value.(int32)), 10)
|
||||
case int64:
|
||||
return strconv.FormatInt(value.(int64), 10)
|
||||
case uint:
|
||||
return strconv.FormatUint(uint64(value.(uint)), 10)
|
||||
case uint8:
|
||||
return strconv.FormatUint(uint64(value.(uint8)), 10)
|
||||
case uint16:
|
||||
return strconv.FormatUint(uint64(value.(uint16)), 10)
|
||||
case uint32:
|
||||
return strconv.FormatUint(uint64(value.(uint32)), 10)
|
||||
case uint64:
|
||||
return strconv.FormatUint(value.(uint64), 10)
|
||||
case string:
|
||||
result = v.String()
|
||||
return result
|
||||
return value.(string)
|
||||
case []byte:
|
||||
result = string(v.Bytes())
|
||||
return result
|
||||
return string(value.([]byte))
|
||||
default:
|
||||
newValue, _ := json.Marshal(value)
|
||||
result = string(newValue)
|
||||
return result
|
||||
return string(newValue)
|
||||
|
||||
// todo: maybe we should't supprt other type convertion
|
||||
// v := reflect.ValueOf(value)
|
||||
// log.Panicf("Unsupported data type: %s ", v.String())
|
||||
// return ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,9 +137,10 @@ func TestToString(t *testing.T) {
|
||||
"", "",
|
||||
"0", "1", "-1",
|
||||
"123", "123", "123", "123", "123", "123", "123",
|
||||
"12.3", "12.300000190734863",
|
||||
"12.3", "12.3",
|
||||
"true", "false",
|
||||
"[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello"}
|
||||
"[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello",
|
||||
}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
actual := ToString(cases[i])
|
||||
|
||||
@@ -4,22 +4,59 @@ package datastructure
|
||||
type Set[T comparable] map[T]struct{}
|
||||
|
||||
// NewSet return a instance of set
|
||||
func NewSet[T comparable](values ...T) Set[T] {
|
||||
func NewSet[T comparable](items ...T) Set[T] {
|
||||
set := make(Set[T])
|
||||
set.Add(values...)
|
||||
set.Add(items...)
|
||||
return set
|
||||
}
|
||||
|
||||
// Add value to set
|
||||
func (s Set[T]) Add(values ...T) {
|
||||
for _, v := range values {
|
||||
// NewSetFromSlice create a set from slice
|
||||
func NewSetFromSlice[T comparable](items []T) Set[T] {
|
||||
set := make(Set[T])
|
||||
for _, item := range items {
|
||||
set.Add(item)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Add items to set
|
||||
func (s Set[T]) Add(items ...T) {
|
||||
for _, v := range items {
|
||||
s[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Contain checks if set contains value or not
|
||||
func (s Set[T]) Contain(value T) bool {
|
||||
_, ok := s[value]
|
||||
// AddIfNotExist checks if item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exist in the set,
|
||||
// or else it does nothing and returns false.
|
||||
func (s Set[T]) AddIfNotExist(item T) bool {
|
||||
if !s.Contain(item) {
|
||||
if _, ok := s[item]; !ok {
|
||||
s[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddIfNotExistBy checks if item exists in the set and pass the `checker` function
|
||||
// it adds the item to set and returns true if it does not exists in the set and
|
||||
// function `checker` returns true, or else it does nothing and returns false.
|
||||
func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool {
|
||||
if !s.Contain(item) {
|
||||
if checker(item) {
|
||||
if _, ok := s[item]; !ok {
|
||||
s[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Contain checks if set contains item or not
|
||||
func (s Set[T]) Contain(item T) bool {
|
||||
_, ok := s[item]
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -41,9 +78,9 @@ func (s Set[T]) Clone() Set[T] {
|
||||
return set
|
||||
}
|
||||
|
||||
// Delete value of set
|
||||
func (s Set[T]) Delete(values ...T) {
|
||||
for _, v := range values {
|
||||
// Delete item of set
|
||||
func (s Set[T]) Delete(items ...T) {
|
||||
for _, v := range items {
|
||||
delete(s, v)
|
||||
}
|
||||
}
|
||||
@@ -58,7 +95,7 @@ func (s Set[T]) Equal(other Set[T]) bool {
|
||||
}
|
||||
|
||||
// Iterate call function by every element of set
|
||||
func (s Set[T]) Iterate(fn func(value T)) {
|
||||
func (s Set[T]) Iterate(fn func(item T)) {
|
||||
for v := range s {
|
||||
fn(v)
|
||||
}
|
||||
@@ -76,13 +113,13 @@ func (s Set[T]) Size() int {
|
||||
|
||||
// Values return all values of set
|
||||
func (s Set[T]) Values() []T {
|
||||
values := make([]T, 0, 0)
|
||||
result := make([]T, 0, len(s))
|
||||
|
||||
s.Iterate(func(value T) {
|
||||
values = append(values, value)
|
||||
result = append(result, value)
|
||||
})
|
||||
|
||||
return values
|
||||
return result
|
||||
}
|
||||
|
||||
// Union creates a new set contain all element of set s and other
|
||||
|
||||
@@ -6,6 +6,19 @@ import (
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestSet_NewSetFromSlice(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSet_NewSetFromSlice")
|
||||
|
||||
s1 := NewSetFromSlice([]int{1, 2, 2, 3})
|
||||
assert.Equal(3, s1.Size())
|
||||
assert.Equal(true, s1.Contain(1))
|
||||
assert.Equal(true, s1.Contain(2))
|
||||
assert.Equal(true, s1.Contain(3))
|
||||
|
||||
s2 := NewSetFromSlice([]int{})
|
||||
assert.Equal(0, s2.Size())
|
||||
}
|
||||
|
||||
func TestSet_Add(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSet_Add")
|
||||
|
||||
@@ -17,6 +30,38 @@ func TestSet_Add(t *testing.T) {
|
||||
assert.Equal(true, set.Equal(expected))
|
||||
}
|
||||
|
||||
func TestSet_AddIfNotExist(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSet_AddIfNotExist")
|
||||
|
||||
set := NewSet[int]()
|
||||
set.Add(1, 2, 3)
|
||||
|
||||
assert.Equal(false, set.AddIfNotExist(1))
|
||||
assert.Equal(true, set.AddIfNotExist(4))
|
||||
assert.Equal(NewSet(1, 2, 3, 4), set)
|
||||
}
|
||||
|
||||
func TestSet_AddIfNotExistBy(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSet_AddIfNotExistBy")
|
||||
|
||||
set := NewSet[int]()
|
||||
set.Add(1, 2)
|
||||
|
||||
ok := set.AddIfNotExistBy(3, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
|
||||
notOk := set.AddIfNotExistBy(4, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal(false, notOk)
|
||||
|
||||
assert.Equal(true, set.Contain(3))
|
||||
assert.Equal(false, set.Contain(4))
|
||||
}
|
||||
|
||||
func TestSet_Contain(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSet_Contain")
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
|
||||
- [BubbleSort](#BubbleSort)
|
||||
- [InsertionSort](#InsertionSort)
|
||||
- [SelectionSort](#SelectionSort)
|
||||
@@ -31,7 +32,6 @@ import (
|
||||
- [CountSort](#CountSort)
|
||||
- [BinarySearch](#BinarySearch)
|
||||
- [BinaryIterativeSearch](#BinaryIterativeSearch)
|
||||
|
||||
- [LinearSearch](#LinearSearch)
|
||||
- [LRUCache](#LRUCache)
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
- [QuickSort](#QuickSort)
|
||||
- [HeapSort](#HeapSort)
|
||||
- [MergeSort](#MergeSort)
|
||||
|
||||
- [CountSort](#CountSort)
|
||||
- [BinarySearch](#BinarySearch)
|
||||
- [BinaryIterativeSearch](#BinaryIterativeSearch)
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
- [ToBytes](#ToBytes)
|
||||
- [ToChar](#ToChar)
|
||||
- [ToChannel](#ToChannel)
|
||||
|
||||
- [ToFloat](#ToFloat)
|
||||
- [ToInt](#ToInt)
|
||||
- [ToJson](#ToJson)
|
||||
@@ -395,6 +394,32 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="ToString">ToString</span>
|
||||
|
||||
<p>ToString convert value to string, for number, string, []byte, will convert to string. For other type (slice, map, array, struct) will call json.Marshal</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ToString(value any) string
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%q", convertor.ToString(1)) //"1"
|
||||
fmt.Printf("%q", convertor.ToString(1.1)) //"1.1"
|
||||
fmt.Printf("%q", convertor.ToString([]int{1, 2, 3})) //"[1,2,3]"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="StructToMap">StructToMap</span>
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
- [ToBytes](#ToBytes)
|
||||
- [ToChar](#ToChar)
|
||||
- [ToChannel](#ToChannel)
|
||||
|
||||
- [ToFloat](#ToFloat)
|
||||
- [ToInt](#ToInt)
|
||||
- [ToJson](#ToJson)
|
||||
@@ -401,7 +400,7 @@ func main() {
|
||||
|
||||
### <span id="ToString">ToString</span>
|
||||
|
||||
<p>将interface转成字符串</p>
|
||||
<p>将值转换为字符串,对于数字、字符串、[]byte,将转换为字符串。 对于其他类型(切片、映射、数组、结构)将调用 json.Marshal</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ import (
|
||||
- [HmacSha1](#HmacSha1)
|
||||
- [HmacSha256](#HmacSha256)
|
||||
- [HmacSha512](#HmacSha512)
|
||||
|
||||
- [Md5String](#Md5String)
|
||||
- [Md5File](#Md5File)
|
||||
- [Sha1](#Sha1)
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
- [AesOfbDecrypt](#AesOfbDecrypt)
|
||||
- [Base64StdEncode](#Base64StdEncode)
|
||||
- [Base64StdDecode](#Base64StdDecode)
|
||||
|
||||
- [DesEcbEncrypt](#DesEcbEncrypt)
|
||||
- [DesEcbDecrypt](#DesEcbDecrypt)
|
||||
- [DesCbcEncrypt](#DesCbcEncrypt)
|
||||
@@ -43,7 +42,6 @@ import (
|
||||
- [DesCfbDecrypt](#DesCfbDecrypt)
|
||||
- [DesOfbEncrypt](#DesOfbEncrypt)
|
||||
- [DesOfbDecrypt](#DesOfbDecrypt)
|
||||
|
||||
- [HmacMd5](#HmacMd5)
|
||||
- [HmacSha1](#HmacSha1)
|
||||
- [HmacSha256](#HmacSha256)
|
||||
|
||||
@@ -22,8 +22,11 @@ import (
|
||||
## Index
|
||||
|
||||
- [NewSet](#NewSet)
|
||||
- [NewSetFromSlice](#NewSetFromSlice)
|
||||
- [Values](#Values)
|
||||
- [Add](#Add)
|
||||
- [AddIfNotExist](#AddIfNotExist)
|
||||
- [AddIfNotExistBy](#AddIfNotExistBy)
|
||||
- [Delete](#Delete)
|
||||
- [Contain](#Contain)
|
||||
- [ContainAll](#ContainAll)
|
||||
@@ -34,7 +37,6 @@ import (
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [Union](#Union)
|
||||
- [Intersection](#Intersection)
|
||||
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
|
||||
@@ -45,13 +47,13 @@ import (
|
||||
## Documentation
|
||||
|
||||
### <span id="NewSet">NewSet</span>
|
||||
<p>Make a Set instance</p>
|
||||
<p>Create a set instance</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type Set[T comparable] map[T]bool
|
||||
func NewSet[T comparable](values ...T) Set[T]
|
||||
func NewSet[T comparable](items ...T) Set[T]
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -70,6 +72,30 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="NewSetFromSlice">NewSetFromSlice</span>
|
||||
<p>Create a set from slice</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func NewSetFromSlice[T comparable](items []T) Set[T]
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Values">Values</span>
|
||||
@@ -100,12 +126,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="Add">Add</span>
|
||||
<p>Add value to set</p>
|
||||
<p>Add items to set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Add(values ...T)
|
||||
func (s Set[T]) Add(items ...T)
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -126,14 +152,83 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Delete">Delete</span>
|
||||
<p>Delete value in set</p>
|
||||
### <span id="AddIfNotExist">AddIfNotExist</span>
|
||||
<p>AddIfNotExist checks if item exists in the set, it adds the item to set and returns true if it does not exist in the set, or else it does nothing and returns false.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Delete(values ...T)
|
||||
func (s Set[T]) AddIfNotExist(item T) bool
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
r1 := st.AddIfNotExist(1)
|
||||
r2 := st.AddIfNotExist(4)
|
||||
|
||||
fmt.Println(r1) // false
|
||||
fmt.Println(r2) // true
|
||||
fmt.Println(st.Values()) // 1,2,3,4
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="AddIfNotExistBy">AddIfNotExistBy</span>
|
||||
<p>AddIfNotExistBy checks if item exists in the set and pass the `checker` function it adds the item to set and returns true if it does not exists in the set and function `checker` returns true, or else it does nothing and returns false.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2)
|
||||
|
||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
fmt.Println(ok) // true
|
||||
|
||||
|
||||
notOk := st.AddIfNotExistBy(4, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
fmt.Println(notOk) // false
|
||||
|
||||
fmt.Println(st.Values()) // 1, 2, 3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Delete">Delete</span>
|
||||
<p>Delete item in set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Delete(items ...T)
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -157,12 +252,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="Contain">Contain</span>
|
||||
<p>Check if value is in set or not</p>
|
||||
<p>Check if item is in set or not</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Contain(value T) bool
|
||||
func (s Set[T]) Contain(item T) bool
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -309,7 +404,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Iterate(fn func(value T))
|
||||
func (s Set[T]) Iterate(fn func(item T))
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -324,8 +419,8 @@ import (
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
arr := []int{}
|
||||
set.Iterate(func(value int) {
|
||||
arr = append(arr, value)
|
||||
set.Iterate(func(item int) {
|
||||
arr = append(arr, item)
|
||||
})
|
||||
|
||||
fmt.Println(arr) //1,2,3
|
||||
|
||||
@@ -22,8 +22,11 @@ import (
|
||||
## 目录
|
||||
|
||||
- [NewSet](#NewSet)
|
||||
- [NewSetFromSlice](#NewSetFromSlice)
|
||||
- [Values](#Values)
|
||||
- [Add](#Add)
|
||||
- [AddIfNotExist](#AddIfNotExist)
|
||||
- [AddIfNotExistBy](#AddIfNotExistBy)
|
||||
- [Delete](#Delete)
|
||||
- [Contain](#Contain)
|
||||
- [ContainAll](#ContainAll)
|
||||
@@ -34,7 +37,6 @@ import (
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [Union](#Union)
|
||||
- [Intersection](#Intersection)
|
||||
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
|
||||
@@ -51,7 +53,7 @@ import (
|
||||
|
||||
```go
|
||||
type Set[T comparable] map[T]bool
|
||||
func NewSet[T comparable](values ...T) Set[T]
|
||||
func NewSet[T comparable](items ...T) Set[T]
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -71,6 +73,31 @@ func main() {
|
||||
|
||||
|
||||
|
||||
### <span id="NewSetFromSlice">NewSetFromSlice</span>
|
||||
<p>基于切片创建集合</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func NewSetFromSlice[T comparable](items []T) Set[T]
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Values">Values</span>
|
||||
<p>获取集合中所有元素的切片</p>
|
||||
@@ -105,7 +132,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Add(values ...T)
|
||||
func (s Set[T]) Add(items ...T)
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -126,6 +153,76 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="AddIfNotExist">AddIfNotExist</span>
|
||||
<p>如果集合中不存在元素,则添加该元素返回true, 如果集合中存在元素, 不做任何操作,返回false</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) AddIfNotExist(item T) bool
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
r1 := st.AddIfNotExist(1)
|
||||
r2 := st.AddIfNotExist(4)
|
||||
|
||||
fmt.Println(r1) // false
|
||||
fmt.Println(r2) // true
|
||||
fmt.Println(st.Values()) // 1,2,3,4
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="AddIfNotExistBy">AddIfNotExistBy</span>
|
||||
<p>根据checker函数判断元素是否在集合中,如果集合中不存在元素且checker返回true,则添加该元素返回true, 否则不做任何操作,返回false</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2)
|
||||
|
||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
fmt.Println(ok) // true
|
||||
|
||||
|
||||
notOk := st.AddIfNotExistBy(4, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
fmt.Println(notOk) // false
|
||||
|
||||
fmt.Println(st.Values()) // 1, 2, 3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Delete">Delete</span>
|
||||
<p>删除集合中元素</p>
|
||||
@@ -133,7 +230,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Delete(values ...T)
|
||||
func (s Set[T]) Delete(items ...T)
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -162,7 +259,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Contain(value T) bool
|
||||
func (s Set[T]) Contain(item T) bool
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -309,7 +406,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Iterate(fn func(value T))
|
||||
func (s Set[T]) Iterate(fn func(item T))
|
||||
```
|
||||
<b>例子:</b>
|
||||
|
||||
@@ -324,8 +421,8 @@ import (
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
arr := []int{}
|
||||
set.Iterate(func(value int) {
|
||||
arr = append(arr, value)
|
||||
set.Iterate(func(item int) {
|
||||
arr = append(arr, item)
|
||||
})
|
||||
|
||||
fmt.Println(arr) //1,2,3
|
||||
@@ -420,9 +517,6 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="SymmetricDifference">SymmetricDifference</span>
|
||||
<p>返回一个集合,其中元素在第一个集合或第二个集合中,且不同时存在于两个集合中</p>
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
- [PreOrderTraverse](#BSTree_PreOrderTraverse)
|
||||
- [InOrderTraverse](#BSTree_InOrderTraverse)
|
||||
- [PostOrderTraverse](#BSTree_PostOrderTraverse)
|
||||
|
||||
- [LevelOrderTraverse](#BSTree_LevelOrderTraverse)
|
||||
- [Depth](#BSTree_Depth)
|
||||
- [HasSubTree](#BSTree_HasSubTree)
|
||||
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
- [PreOrderTraverse](#BSTree_PreOrderTraverse)
|
||||
- [InOrderTraverse](#BSTree_InOrderTraverse)
|
||||
- [PostOrderTraverse](#BSTree_PostOrderTraverse)
|
||||
|
||||
- [LevelOrderTraverse](#BSTree_LevelOrderTraverse)
|
||||
- [Depth](#BSTree_Depth)
|
||||
- [HasSubTree](#BSTree_HasSubTree)
|
||||
|
||||
@@ -42,7 +42,6 @@ import (
|
||||
- [GetNightTimestamp](#GetNightTimestamp)
|
||||
- [FormatTimeToStr](#FormatTimeToStr)
|
||||
- [FormatStrToTime](#FormatStrToTime)
|
||||
|
||||
- [NewUnixNow](#NewUnixNow)
|
||||
- [NewUnix](#NewUnix)
|
||||
- [NewFormat](#NewFormat)
|
||||
|
||||
@@ -41,7 +41,6 @@ import (
|
||||
- [GetNightTimestamp](#GetNightTimestamp)
|
||||
- [FormatTimeToStr](#FormatTimeToStr)
|
||||
- [FormatStrToTime](#FormatStrToTime)
|
||||
|
||||
- [NewUnixNow](#NewUnixNow)
|
||||
- [NewUnix](#NewUnix)
|
||||
- [NewFormat](#NewFormat)
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
- [IsExist](#IsExist)
|
||||
- [IsLink](#IsLink)
|
||||
- [IsDir](#IsDir)
|
||||
|
||||
- [ListFileNames](#ListFileNames)
|
||||
- [RemoveFile](#RemoveFile)
|
||||
- [ReadFileToString](#ReadFileToString)
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
- [IsExist](#IsExist)
|
||||
- [IsLink](#IsLink)
|
||||
- [IsDir](#IsDir)
|
||||
|
||||
- [ListFileNames](#ListFileNames)
|
||||
- [RemoveFile](#RemoveFile)
|
||||
- [ReadFileToString](#ReadFileToString)
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
- [MaxBy](#MaxBy)
|
||||
- [Min](#Min)
|
||||
- [MinBy](#MaxBy)
|
||||
|
||||
- [Percent](#Percent)
|
||||
- [RoundToFloat](#RoundToFloat)
|
||||
- [RoundToString](#RoundToString)
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
- [MaxBy](#MaxBy)
|
||||
- [Min](#Min)
|
||||
- [MinBy](#MaxBy)
|
||||
|
||||
- [Percent](#Percent)
|
||||
- [RoundToFloat](#RoundToFloat)
|
||||
- [RoundToString](#RoundToString)
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
## Index
|
||||
- [ConvertMapToQueryString](#ConvertMapToQueryString)
|
||||
- [EncodeUrl](#EncodeUrl)
|
||||
|
||||
- [GetInternalIp](#GetInternalIp)
|
||||
- [GetIps](#GetIps)
|
||||
- [GetMacAddrs](#GetMacAddrs)
|
||||
@@ -38,7 +37,6 @@ import (
|
||||
- [SendRequest](#SendRequest)
|
||||
- [DecodeResponse](#DecodeResponse)
|
||||
- [StructToUrlValues](#StructToUrlValues)
|
||||
|
||||
- [HttpGet<sup>Deprecated</sup>](#HttpGet)
|
||||
- [HttpDelete<sup>Deprecated</sup>](#HttpDelete)
|
||||
- [HttpPost<sup>Deprecated</sup>](#HttpPost)
|
||||
|
||||
@@ -30,7 +30,6 @@ import (
|
||||
- [GetMacAddrs](#GetMacAddrs)
|
||||
- [GetPublicIpInfo](#GetPublicIpInfo)
|
||||
- [GetRequestPublicIp](#GetRequestPublicIp)
|
||||
|
||||
- [IsPublicIP](#IsPublicIP)
|
||||
- [IsInternalIP](#IsInternalIP)
|
||||
- [HttpRequest](#HttpRequest)
|
||||
@@ -38,13 +37,11 @@ import (
|
||||
- [SendRequest](#SendRequest)
|
||||
- [DecodeResponse](#DecodeResponse)
|
||||
- [StructToUrlValues](#StructToUrlValues)
|
||||
|
||||
- [HttpGet<sup>Deprecated</sup>](#HttpGet)
|
||||
- [HttpDelete<sup>Deprecated</sup>](#HttpDelete)
|
||||
- [HttpPost<sup>Deprecated</sup>](#HttpPost)
|
||||
- [HttpPut<sup>Deprecated</sup>](#HttpPut)
|
||||
- [HttpPatch<sup>Deprecated</sup>](#HttpPatch)
|
||||
|
||||
- [ParseHttpResponse](#ParseHttpResponse)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
- [RandInt](#RandInt)
|
||||
- [RandString](#RandString)
|
||||
- [RandUpper](#RandUpper)
|
||||
|
||||
- [RandLower](#RandLower)
|
||||
- [RandNumeral](#RandNumeral)
|
||||
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
- [RandInt](#RandInt)
|
||||
- [RandString](#RandString)
|
||||
- [RandUpper](#RandUpper)
|
||||
|
||||
- [RandLower](#RandLower)
|
||||
- [RandNumeral](#RandNumeral)
|
||||
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
||||
|
||||
@@ -43,7 +43,6 @@ import (
|
||||
- [Flatten](#Flatten)
|
||||
- [FlattenDeep](#FlattenDeep)
|
||||
- [ForEach](#ForEach)
|
||||
|
||||
- [GroupBy](#GroupBy)
|
||||
- [GroupWith](#GroupWith)
|
||||
- [IntSlice](#IntSlice)
|
||||
@@ -53,10 +52,12 @@ import (
|
||||
- [IndexOf](#IndexOf)
|
||||
- [LastIndexOf](#LastIndexOf)
|
||||
- [Map](#Map)
|
||||
- [Merge](#Merge)
|
||||
- [Reverse](#Reverse)
|
||||
- [Reduce](#Reduce)
|
||||
- [Replace](#Replace)
|
||||
- [ReplaceAll](#ReplaceAll)
|
||||
- [Repeat](#Repeat)
|
||||
- [Shuffle](#Shuffle)
|
||||
- [SortByField](#SortByField)
|
||||
- [Some](#Some)
|
||||
@@ -172,8 +173,8 @@ import (
|
||||
|
||||
func main() {
|
||||
arr := []string{"a", "b", "c", "d", "e"}
|
||||
res := slice.Chunk(InterfaceSlice(arr), 3)
|
||||
fmt.Println(res) //[][]any{{"a", "b", "c"}, {"d", "e"}}
|
||||
res := slice.Chunk((arr), 3)
|
||||
fmt.Println(res) //[][]string{{"a", "b", "c"}, {"d", "e"}}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -184,7 +185,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Compact[T any](slice []T) []T
|
||||
func Compact[T comparable](slice []T) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
@@ -909,6 +910,36 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Merge">Merge</span>
|
||||
|
||||
<p>Merge all given slices into one slice.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Merge[T any](slices ...[]T) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s1 := []int{1, 2, 3}
|
||||
s2 := []int{2, 4}
|
||||
res := slice.Merge(s1, s2)
|
||||
|
||||
fmt.Println(res) //[]int{1, 2, 3, 2, 4}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Reverse">Reverse</span>
|
||||
|
||||
<p>Reverse the elements order in slice.</p>
|
||||
@@ -1018,6 +1049,31 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Repeat">Repeat</span>
|
||||
|
||||
<p>Creates a slice with length n whose elements are passed param item.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Repeat[T any](item T, n int) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(slice.Repeat("a", 3)) //[]string{"a", "a", "a"}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Shuffle">Shuffle</span>
|
||||
|
||||
<p>Creates an slice of shuffled values.</p>
|
||||
|
||||
@@ -20,8 +20,10 @@ import (
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
|
||||
## 目录
|
||||
|
||||
|
||||
- [AppendIfAbsent](#AppendIfAbsent)
|
||||
- [Contain](#Contain)
|
||||
- [ContainSubSlice](#ContainSubSlice)
|
||||
@@ -46,17 +48,18 @@ import (
|
||||
- [GroupBy](#GroupBy)
|
||||
- [GroupWith](#GroupWith)
|
||||
- [IntSlice](#IntSlice)
|
||||
|
||||
- [InterfaceSlice](#InterfaceSlice)
|
||||
- [Intersection](#Intersection)
|
||||
- [InsertAt](#InsertAt)
|
||||
- [IndexOf](#IndexOf)
|
||||
- [LastIndexOf](#LastIndexOf)
|
||||
- [Map](#Map)
|
||||
- [Merge](#Merge)
|
||||
- [Reverse](#Reverse)
|
||||
- [Reduce](#Reduce)
|
||||
- [Replace](#Replace)
|
||||
- [ReplaceAll](#ReplaceAll)
|
||||
- [Repeat](#Repeat)
|
||||
- [Shuffle](#Shuffle)
|
||||
- [SortByField](#SortByField)
|
||||
- [Some](#Some)
|
||||
@@ -72,6 +75,7 @@ import (
|
||||
- [Without](#Without)
|
||||
- [KeyBy](#KeyBy)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
@@ -172,8 +176,8 @@ import (
|
||||
|
||||
func main() {
|
||||
arr := []string{"a", "b", "c", "d", "e"}
|
||||
res := slice.Chunk(InterfaceSlice(arr), 3)
|
||||
fmt.Println(res) //[][]any{{"a", "b", "c"}, {"d", "e"}}
|
||||
res := slice.Chunk(arr, 3)
|
||||
fmt.Println(res) //[][]string{{"a", "b", "c"}, {"d", "e"}}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -184,7 +188,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Compact[T any](slice []T) []T
|
||||
func Compact[T comparable](slice []T) []T
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
@@ -883,7 +887,7 @@ func main() {
|
||||
|
||||
### <span id="Map">Map</span>
|
||||
|
||||
<p>通过运行函数slice中的每个元素来创建一个新切片</p>
|
||||
<p>对slice中的每个元素执行map函数以创建一个新切片</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -909,6 +913,34 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Merge">Merge</span>
|
||||
|
||||
<p>合并多个切片(不会消除重复元素).</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Merge[T any](slices ...[]T) []T
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s1 := []int{1, 2, 3}
|
||||
s2 := []int{2, 4}
|
||||
res := slice.Merge(s1, s2)
|
||||
|
||||
fmt.Println(res) //[]int{1, 2, 3, 2, 4}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Reverse">Reverse</span>
|
||||
|
||||
<p>反转切片中的元素顺序</p>
|
||||
@@ -1018,6 +1050,30 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Repeat">Repeat</span>
|
||||
|
||||
<p>创建一个切片,包含n个传入的item</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Repeat[T any](item T, n int) []T
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(slice.Repeat("a", 3)) //[]string{"a", "a", "a"}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Shuffle">Shuffle</span>
|
||||
|
||||
<p>随机打乱切片中的元素顺序</p>
|
||||
|
||||
@@ -36,7 +36,6 @@ import (
|
||||
- [SnakeCase](#SnakeCase)
|
||||
- [SplitEx](#SplitEx)
|
||||
- [Wrap](#Wrap)
|
||||
|
||||
- [Unwrap](#Unwrap)
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ import (
|
||||
- [SnakeCase](#SnakeCase)
|
||||
- [SplitEx](#SplitEx)
|
||||
- [Wrap](#Wrap)
|
||||
|
||||
- [Unwrap](#Unwrap)
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ import (
|
||||
- [IsCreditCard](#IsCreditCard)
|
||||
- [IsDns](#IsDns)
|
||||
- [IsEmail](#IsEmail)
|
||||
|
||||
- [IsEmptyString](#IsEmptyString)
|
||||
- [IsFloatStr](#IsFloatStr)
|
||||
- [IsNumberStr](#IsNumberStr)
|
||||
|
||||
@@ -34,7 +34,6 @@ import (
|
||||
- [IsCreditCard](#IsCreditCard)
|
||||
- [IsDns](#IsDns)
|
||||
- [IsEmail](#IsEmail)
|
||||
|
||||
- [IsEmptyString](#IsEmptyString)
|
||||
- [IsFloatStr](#IsFloatStr)
|
||||
- [IsNumberStr](#IsNumberStr)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
@@ -213,6 +214,7 @@ func Zip(fpath string, destPath string) error {
|
||||
|
||||
// UnZip unzip the file and save it to destPath
|
||||
func UnZip(zipFile string, destPath string) error {
|
||||
|
||||
zipReader, err := zip.OpenReader(zipFile)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -220,7 +222,12 @@ func UnZip(zipFile string, destPath string) error {
|
||||
defer zipReader.Close()
|
||||
|
||||
for _, f := range zipReader.File {
|
||||
path := filepath.Join(destPath, f.Name)
|
||||
//issue#62: fix ZipSlip bug
|
||||
path, err := safeFilepathJoin(destPath, f.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(path, os.ModePerm)
|
||||
} else {
|
||||
@@ -249,6 +256,17 @@ func UnZip(zipFile string, destPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func safeFilepathJoin(path1, path2 string) (string, error) {
|
||||
relPath, err := filepath.Rel(".", path2)
|
||||
if err != nil || strings.HasPrefix(relPath, "..") {
|
||||
return "", fmt.Errorf("(zipslip) filepath is unsafe %q: %v", path2, err)
|
||||
}
|
||||
if path1 == "" {
|
||||
path1 = "."
|
||||
}
|
||||
return filepath.Join(path1, filepath.Join("/", relPath)), nil
|
||||
}
|
||||
|
||||
// IsLink checks if a file is symbol link or not
|
||||
func IsLink(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
|
||||
174
iterator/iterator.go
Normal file
174
iterator/iterator.go
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2022 dudaodong@gmail.com. All rights resulterved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// 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.
|
||||
// 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"
|
||||
|
||||
// Iterator supports iterating over a sequence of values of type `E`.
|
||||
type Iterator[T any] interface {
|
||||
// Next checks if there is a next value in the iteration or not
|
||||
HasNext() bool
|
||||
// Next returns the next value in the iteration if there is one,
|
||||
// and reports whether the returned value is valid.
|
||||
// Once Next returns ok==false, the iteration is over,
|
||||
// and all subsequent calls will return ok==false.
|
||||
Next() (item T, ok bool)
|
||||
}
|
||||
|
||||
// StopIterator is an interface for stopping Iterator.
|
||||
type StopIterator[T any] interface {
|
||||
Iterator[T]
|
||||
|
||||
// Stop indicates that the iterator will no longer be used.
|
||||
// After a call to Stop, future calls to Next may panic.
|
||||
// Stop may be called multiple times;
|
||||
// all calls after the first will have no effect.
|
||||
Stop()
|
||||
}
|
||||
|
||||
// DeleteIter is an Iter that implements a Delete method.
|
||||
type DeleteIterator[T any] interface {
|
||||
Iterator[T]
|
||||
|
||||
// Delete deletes the current iterator element;
|
||||
// that is, the one returned by the last call to Next.
|
||||
// Delete should panic if called before Next or after
|
||||
// Next returns false.
|
||||
Delete()
|
||||
}
|
||||
|
||||
// SetIterator is an Iter that implements a Set method.
|
||||
type SetIterator[T any] interface {
|
||||
Iterator[T]
|
||||
|
||||
// Set replaces the current iterator element with v.
|
||||
// Set should panic if called before Next or after
|
||||
// Next returns false.
|
||||
Set(v T)
|
||||
}
|
||||
|
||||
// PrevIterator is an iterator with a Prev method.
|
||||
type PrevIterator[T any] interface {
|
||||
Iterator[T]
|
||||
|
||||
// Prev moves the iterator to the previous position.
|
||||
// After calling Prev, Next will return the value at
|
||||
// that position in the container. For example, after
|
||||
// it.Next() returning (v, true)
|
||||
// it.Prev()
|
||||
// another call to it.Next will again return (v, true).
|
||||
// Calling Prev before calling Next may panic.
|
||||
// Calling Prev after Next returns false will move
|
||||
// to the last element, or, if there are no elements,
|
||||
// to the iterator's initial state.
|
||||
Prev()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Functions that create an Iterator from some other type. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FromSlice returns an iterator over a slice of data.
|
||||
func FromSlice[T any](slice []T) Iterator[T] {
|
||||
return &sliceIterator[T]{slice: slice, index: -1}
|
||||
}
|
||||
|
||||
func ToSlice[T any](iter Iterator[T]) []T {
|
||||
result := []T{}
|
||||
for item, ok := iter.Next(); ok; item, ok = iter.Next() {
|
||||
result = append(result, item)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type sliceIterator[T any] struct {
|
||||
slice []T
|
||||
index int
|
||||
}
|
||||
|
||||
func (iter *sliceIterator[T]) HasNext() bool {
|
||||
return iter.index < len(iter.slice)-1
|
||||
}
|
||||
|
||||
func (iter *sliceIterator[T]) Next() (T, bool) {
|
||||
iter.index++
|
||||
ok := iter.index >= 0 && iter.index < len(iter.slice)
|
||||
var item T
|
||||
if ok {
|
||||
item = iter.slice[iter.index]
|
||||
}
|
||||
return item, ok
|
||||
|
||||
// if len(iter.slice) == 0 {
|
||||
// var zero T
|
||||
// return zero, false
|
||||
// }
|
||||
// iter.index++
|
||||
// item := iter.slice[0]
|
||||
// iter.slice = iter.slice[1:]
|
||||
// return item, true
|
||||
}
|
||||
|
||||
// Prev implements PrevIterator.
|
||||
func (iter *sliceIterator[T]) Prev() {
|
||||
if iter.index == -1 {
|
||||
panic("Next function should be called Prev")
|
||||
}
|
||||
if iter.HasNext() {
|
||||
iter.index--
|
||||
} else {
|
||||
iter.index = len(iter.slice) - 1
|
||||
}
|
||||
}
|
||||
|
||||
// Set implements SetIterator.
|
||||
func (iter *sliceIterator[T]) Set(value T) {
|
||||
if iter.index == -1 {
|
||||
panic("Next function should be called Set")
|
||||
}
|
||||
if iter.index >= len(iter.slice) || len(iter.slice) == 0 {
|
||||
panic("No element in current iterator")
|
||||
}
|
||||
iter.slice[iter.index] = value
|
||||
}
|
||||
|
||||
// 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] {
|
||||
if end < start {
|
||||
panic("RangeIterator: start should be before end")
|
||||
} else if step <= 0 {
|
||||
panic("RangeIterator: step should be positive")
|
||||
}
|
||||
|
||||
return &rangeIterator[T]{start: start, end: end, step: step}
|
||||
}
|
||||
|
||||
type rangeIterator[T lancetconstraints.Number] struct {
|
||||
start, end, step T
|
||||
}
|
||||
|
||||
func (iter *rangeIterator[T]) HasNext() bool {
|
||||
if iter.start >= iter.end {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (iter *rangeIterator[T]) Next() (T, bool) {
|
||||
if iter.start >= iter.end {
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
num := iter.start
|
||||
iter.start += iter.step
|
||||
return num, true
|
||||
}
|
||||
50
iterator/iterator_test.go
Normal file
50
iterator/iterator_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2022 dudaodong@gmail.com. All rights resulterved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package iterator implements some feature of C++ STL iterators
|
||||
package iterator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestSliceIterator(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSliceIterator")
|
||||
|
||||
// HashNext
|
||||
t.Run("slice iterator HasNext: ", func(t *testing.T) {
|
||||
iter1 := FromSlice([]int{1, 2, 3, 4})
|
||||
for {
|
||||
item, _ := iter1.Next()
|
||||
|
||||
if item == 4 {
|
||||
assert.Equal(false, iter1.HasNext())
|
||||
break
|
||||
} else {
|
||||
assert.Equal(true, iter1.HasNext())
|
||||
}
|
||||
}
|
||||
|
||||
iter2 := FromSlice([]int{})
|
||||
assert.Equal(false, iter2.HasNext())
|
||||
})
|
||||
|
||||
//Next
|
||||
t.Run("slice iterator Next: ", func(t *testing.T) {
|
||||
iter1 := FromSlice([]int{1, 2, 3, 4})
|
||||
for i := 0; i < 4; i++ {
|
||||
item, ok := iter1.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
assert.Equal(i+1, item)
|
||||
}
|
||||
|
||||
iter2 := FromSlice([]int{})
|
||||
_, ok := iter2.Next()
|
||||
assert.Equal(false, ok)
|
||||
})
|
||||
|
||||
}
|
||||
61
iterator/operation.go
Normal file
61
iterator/operation.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2022 dudaodong@gmail.com. All rights resulterved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// 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.
|
||||
// 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
|
||||
|
||||
// Map creates a new iterator which applies a function to all items of input iterator.
|
||||
func Map[T any, U any](iter Iterator[T], iteratee func(item T) U) Iterator[U] {
|
||||
return &mapIterator[T, U]{
|
||||
iter: iter,
|
||||
iteratee: iteratee,
|
||||
}
|
||||
}
|
||||
|
||||
type mapIterator[T any, U any] struct {
|
||||
iter Iterator[T]
|
||||
iteratee func(T) U
|
||||
}
|
||||
|
||||
func (mr *mapIterator[T, U]) HasNext() bool {
|
||||
return mr.iter.HasNext()
|
||||
}
|
||||
|
||||
func (mr *mapIterator[T, U]) Next() (U, bool) {
|
||||
var zero U
|
||||
item, ok := mr.iter.Next()
|
||||
if !ok {
|
||||
return zero, false
|
||||
}
|
||||
return mr.iteratee(item), true
|
||||
}
|
||||
|
||||
// Filter creates a new iterator that returns only the items that pass specified predicate function.
|
||||
func Filter[T any](iter Iterator[T], predicateFunc func(item T) bool) Iterator[T] {
|
||||
return &filterIterator[T]{iter: iter, predicateFunc: predicateFunc}
|
||||
}
|
||||
|
||||
type filterIterator[T any] struct {
|
||||
iter Iterator[T]
|
||||
predicateFunc func(T) bool
|
||||
}
|
||||
|
||||
func (fr *filterIterator[T]) Next() (T, bool) {
|
||||
for item, ok := fr.iter.Next(); ok; item, ok = fr.iter.Next() {
|
||||
if fr.predicateFunc(item) {
|
||||
return item, true
|
||||
}
|
||||
}
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
|
||||
func (fr *filterIterator[T]) HasNext() bool {
|
||||
return fr.iter.HasNext()
|
||||
}
|
||||
@@ -12,15 +12,15 @@ import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Contain check if the value is in the slice or not
|
||||
func Contain[T comparable](slice []T, value T) bool {
|
||||
set := make(map[T]struct{}, len(slice))
|
||||
for _, v := range slice {
|
||||
set[v] = struct{}{}
|
||||
// Contain check if the target value is in the slice or not
|
||||
func Contain[T comparable](slice []T, target T) bool {
|
||||
for _, item := range slice {
|
||||
if item == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
_, ok := set[value]
|
||||
return ok
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainSubSlice check if the slice contain subslice or not
|
||||
@@ -42,40 +42,28 @@ func Chunk[T any](slice []T, size int) [][]T {
|
||||
return result
|
||||
}
|
||||
|
||||
length := len(slice)
|
||||
if size == 1 || size >= length {
|
||||
for _, v := range slice {
|
||||
var tmp []T
|
||||
tmp = append(tmp, v)
|
||||
result = append(result, tmp)
|
||||
for _, item := range slice {
|
||||
l := len(result)
|
||||
if l == 0 || len(result[l-1]) == size {
|
||||
result = append(result, []T{})
|
||||
l++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// divide slice equally
|
||||
divideNum := length/size + 1
|
||||
for i := 0; i < divideNum; i++ {
|
||||
if i == divideNum-1 {
|
||||
if len(slice[i*size:]) > 0 {
|
||||
result = append(result, slice[i*size:])
|
||||
}
|
||||
} else {
|
||||
result = append(result, slice[i*size:(i+1)*size])
|
||||
}
|
||||
result[l-1] = append(result[l-1], item)
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey
|
||||
func Compact[T any](slice []T) []T {
|
||||
result := make([]T, 0)
|
||||
for _, v := range slice {
|
||||
if !reflect.DeepEqual(v, nil) &&
|
||||
!reflect.DeepEqual(v, false) &&
|
||||
!reflect.DeepEqual(v, "") &&
|
||||
!reflect.DeepEqual(v, 0) {
|
||||
result = append(result, v)
|
||||
func Compact[T comparable](slice []T) []T {
|
||||
var zero T
|
||||
|
||||
result := []T{}
|
||||
for _, item := range slice {
|
||||
if item != zero {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,33 +169,29 @@ func EqualWith[T, U any](slice1 []T, slice2 []U, comparator func(T, U) bool) boo
|
||||
|
||||
// Every return true if all of the values in the slice pass the predicate function.
|
||||
func Every[T any](slice []T, predicate func(index int, item T) bool) bool {
|
||||
var currentLength int
|
||||
|
||||
for i, v := range slice {
|
||||
if predicate(i, v) {
|
||||
currentLength++
|
||||
if !predicate(i, v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return currentLength == len(slice)
|
||||
return true
|
||||
}
|
||||
|
||||
// None return true if all the values in the slice mismatch the criteria
|
||||
func None[T any](slice []T, predicate func(index int, item T) bool) bool {
|
||||
|
||||
var currentLength int
|
||||
l := 0
|
||||
for i, v := range slice {
|
||||
if !predicate(i, v) {
|
||||
currentLength++
|
||||
l++
|
||||
}
|
||||
}
|
||||
|
||||
return currentLength == len(slice)
|
||||
return l == len(slice)
|
||||
}
|
||||
|
||||
// Some return true if any of the values in the list pass the predicate function.
|
||||
func Some[T any](slice []T, predicate func(index int, item T) bool) bool {
|
||||
|
||||
for i, v := range slice {
|
||||
if predicate(i, v) {
|
||||
return true
|
||||
@@ -432,6 +416,15 @@ func ReplaceAll[T comparable](slice []T, old T, new T) []T {
|
||||
return Replace(slice, old, new, -1)
|
||||
}
|
||||
|
||||
// Repeat creates a slice with length n whose elements are param `item`.
|
||||
func Repeat[T any](item T, n int) []T {
|
||||
result := make([]T, n)
|
||||
for i := range result {
|
||||
result[i] = item
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// InterfaceSlice convert param to slice of interface.
|
||||
func InterfaceSlice(slice any) []any {
|
||||
sv := sliceValue(slice)
|
||||
@@ -629,6 +622,17 @@ func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T {
|
||||
return result
|
||||
}
|
||||
|
||||
// Merge all given slices into one slice
|
||||
func Merge[T any](slices ...[]T) []T {
|
||||
result := make([]T, 0)
|
||||
|
||||
for _, item := range slices {
|
||||
result = append(result, item...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersection creates a slice of unique values that included by all slices.
|
||||
func Intersection[T comparable](slices ...[]T) []T {
|
||||
if len(slices) == 0 {
|
||||
|
||||
@@ -31,6 +31,10 @@ func TestChunk(t *testing.T) {
|
||||
|
||||
arr := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
assert.Equal([][]string{}, Chunk(arr, -1))
|
||||
|
||||
assert.Equal([][]string{}, Chunk(arr, 0))
|
||||
|
||||
r1 := [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
|
||||
assert.Equal(r1, Chunk(arr, 1))
|
||||
|
||||
@@ -43,8 +47,11 @@ func TestChunk(t *testing.T) {
|
||||
r4 := [][]string{{"a", "b", "c", "d"}, {"e"}}
|
||||
assert.Equal(r4, Chunk(arr, 4))
|
||||
|
||||
r5 := [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
|
||||
r5 := [][]string{{"a", "b", "c", "d", "e"}}
|
||||
assert.Equal(r5, Chunk(arr, 5))
|
||||
|
||||
r6 := [][]string{{"a", "b", "c", "d", "e"}}
|
||||
assert.Equal(r6, Chunk(arr, 6))
|
||||
}
|
||||
|
||||
func TestCompact(t *testing.T) {
|
||||
@@ -439,6 +446,18 @@ func TestUnionBy(t *testing.T) {
|
||||
assert.Equal(result, []int{0, 2, 4, 10})
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestMerge")
|
||||
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
s2 := []int{2, 3, 4, 5}
|
||||
s3 := []int{4, 5, 6}
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4, 2, 3, 4, 5, 4, 5, 6}, Merge(s1, s2, s3))
|
||||
assert.Equal([]int{1, 2, 3, 4, 2, 3, 4, 5}, Merge(s1, s2))
|
||||
assert.Equal([]int{2, 3, 4, 5, 4, 5, 6}, Merge(s2, s3))
|
||||
}
|
||||
|
||||
func TestIntersection(t *testing.T) {
|
||||
s1 := []int{1, 2, 2, 3}
|
||||
s2 := []int{1, 2, 3, 4}
|
||||
@@ -665,3 +684,11 @@ func TestKeyBy(t *testing.T) {
|
||||
|
||||
assert.Equal(result, map[int]string{1: "a", 2: "ab", 3: "abc"})
|
||||
}
|
||||
|
||||
func TestRepeat(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRepeat")
|
||||
|
||||
result := Repeat("a", 6)
|
||||
|
||||
assert.Equal(result, []string{"a", "a", "a", "a", "a", "a"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user