mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
17 Commits
v2.1.9
...
5692982dd1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5692982dd1 | ||
|
|
c39c8914fb | ||
|
|
29bdca1bd2 | ||
|
|
eb66d038ac | ||
|
|
a99ada5ee1 | ||
|
|
a87faf5453 | ||
|
|
ab6fec2f69 | ||
|
|
7b290989f5 | ||
|
|
5722c724e6 | ||
|
|
f84584ca04 | ||
|
|
80cbbdc787 | ||
|
|
be148e07ba | ||
|
|
d36ab5cc3a | ||
|
|
2b17329094 | ||
|
|
f869a0a670 | ||
|
|
be000a4bd6 | ||
|
|
81efa800ea |
@@ -4,7 +4,7 @@
|
|||||||
<br/>
|
<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://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||||
[](https://goreportcard.com/report/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)
|
[](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.
|
Lancet is a comprehensive, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
English | [简体中文](./README_zh-CN.md)
|
English | [简体中文](./README_zh-CN.md) | [Website](https://uvdream.github.io/lancet-docs/en/)
|
||||||
|
|
||||||
## Feature
|
## 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
|
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
|
||||||
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.3 // below go1.18, install latest version of v1.x.x
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<br/>
|
<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://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||||
[](https://goreportcard.com/report/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)
|
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
|
lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
|
||||||
</p>
|
</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
|
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
|
||||||
go get github.com/duke-git/lancet@v1.3.3 // 使用go1.18以下版本, 必须安装v1.x.x版本
|
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.
|
||||||
@@ -4,19 +4,47 @@ package datastructure
|
|||||||
type Set[T comparable] map[T]struct{}
|
type Set[T comparable] map[T]struct{}
|
||||||
|
|
||||||
// NewSet return a instance of set
|
// 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 := make(Set[T])
|
||||||
set.Add(values...)
|
set.Add(items...)
|
||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add value to set
|
// Add items to set
|
||||||
func (s Set[T]) Add(values ...T) {
|
func (s Set[T]) Add(items ...T) {
|
||||||
for _, v := range values {
|
for _, v := range items {
|
||||||
s[v] = struct{}{}
|
s[v] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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(value T) bool {
|
||||||
|
if !s.Contain(value) {
|
||||||
|
if _, ok := s[value]; !ok {
|
||||||
|
s[value] = 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 value or not
|
// Contain checks if set contains value or not
|
||||||
func (s Set[T]) Contain(value T) bool {
|
func (s Set[T]) Contain(value T) bool {
|
||||||
_, ok := s[value]
|
_, ok := s[value]
|
||||||
@@ -41,9 +69,9 @@ func (s Set[T]) Clone() Set[T] {
|
|||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete value of set
|
// Delete item of set
|
||||||
func (s Set[T]) Delete(values ...T) {
|
func (s Set[T]) Delete(items ...T) {
|
||||||
for _, v := range values {
|
for _, v := range items {
|
||||||
delete(s, v)
|
delete(s, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,13 +104,13 @@ func (s Set[T]) Size() int {
|
|||||||
|
|
||||||
// Values return all values of set
|
// Values return all values of set
|
||||||
func (s Set[T]) Values() []T {
|
func (s Set[T]) Values() []T {
|
||||||
values := make([]T, 0, 0)
|
result := make([]T, 0, len(s))
|
||||||
|
|
||||||
s.Iterate(func(value T) {
|
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
|
// Union creates a new set contain all element of set s and other
|
||||||
|
|||||||
@@ -17,6 +17,38 @@ func TestSet_Add(t *testing.T) {
|
|||||||
assert.Equal(true, set.Equal(expected))
|
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) {
|
func TestSet_Contain(t *testing.T) {
|
||||||
assert := internal.NewAssert(t, "TestSet_Contain")
|
assert := internal.NewAssert(t, "TestSet_Contain")
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
## Index
|
## Index
|
||||||
|
|
||||||
- [BubbleSort](#BubbleSort)
|
- [BubbleSort](#BubbleSort)
|
||||||
- [InsertionSort](#InsertionSort)
|
- [InsertionSort](#InsertionSort)
|
||||||
- [SelectionSort](#SelectionSort)
|
- [SelectionSort](#SelectionSort)
|
||||||
@@ -31,7 +32,6 @@ import (
|
|||||||
- [CountSort](#CountSort)
|
- [CountSort](#CountSort)
|
||||||
- [BinarySearch](#BinarySearch)
|
- [BinarySearch](#BinarySearch)
|
||||||
- [BinaryIterativeSearch](#BinaryIterativeSearch)
|
- [BinaryIterativeSearch](#BinaryIterativeSearch)
|
||||||
|
|
||||||
- [LinearSearch](#LinearSearch)
|
- [LinearSearch](#LinearSearch)
|
||||||
- [LRUCache](#LRUCache)
|
- [LRUCache](#LRUCache)
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import (
|
|||||||
- [QuickSort](#QuickSort)
|
- [QuickSort](#QuickSort)
|
||||||
- [HeapSort](#HeapSort)
|
- [HeapSort](#HeapSort)
|
||||||
- [MergeSort](#MergeSort)
|
- [MergeSort](#MergeSort)
|
||||||
|
|
||||||
- [CountSort](#CountSort)
|
- [CountSort](#CountSort)
|
||||||
- [BinarySearch](#BinarySearch)
|
- [BinarySearch](#BinarySearch)
|
||||||
- [BinaryIterativeSearch](#BinaryIterativeSearch)
|
- [BinaryIterativeSearch](#BinaryIterativeSearch)
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import (
|
|||||||
- [ToBytes](#ToBytes)
|
- [ToBytes](#ToBytes)
|
||||||
- [ToChar](#ToChar)
|
- [ToChar](#ToChar)
|
||||||
- [ToChannel](#ToChannel)
|
- [ToChannel](#ToChannel)
|
||||||
|
|
||||||
- [ToFloat](#ToFloat)
|
- [ToFloat](#ToFloat)
|
||||||
- [ToInt](#ToInt)
|
- [ToInt](#ToInt)
|
||||||
- [ToJson](#ToJson)
|
- [ToJson](#ToJson)
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import (
|
|||||||
- [ToBytes](#ToBytes)
|
- [ToBytes](#ToBytes)
|
||||||
- [ToChar](#ToChar)
|
- [ToChar](#ToChar)
|
||||||
- [ToChannel](#ToChannel)
|
- [ToChannel](#ToChannel)
|
||||||
|
|
||||||
- [ToFloat](#ToFloat)
|
- [ToFloat](#ToFloat)
|
||||||
- [ToInt](#ToInt)
|
- [ToInt](#ToInt)
|
||||||
- [ToJson](#ToJson)
|
- [ToJson](#ToJson)
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ import (
|
|||||||
- [HmacSha1](#HmacSha1)
|
- [HmacSha1](#HmacSha1)
|
||||||
- [HmacSha256](#HmacSha256)
|
- [HmacSha256](#HmacSha256)
|
||||||
- [HmacSha512](#HmacSha512)
|
- [HmacSha512](#HmacSha512)
|
||||||
|
|
||||||
- [Md5String](#Md5String)
|
- [Md5String](#Md5String)
|
||||||
- [Md5File](#Md5File)
|
- [Md5File](#Md5File)
|
||||||
- [Sha1](#Sha1)
|
- [Sha1](#Sha1)
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import (
|
|||||||
- [AesOfbDecrypt](#AesOfbDecrypt)
|
- [AesOfbDecrypt](#AesOfbDecrypt)
|
||||||
- [Base64StdEncode](#Base64StdEncode)
|
- [Base64StdEncode](#Base64StdEncode)
|
||||||
- [Base64StdDecode](#Base64StdDecode)
|
- [Base64StdDecode](#Base64StdDecode)
|
||||||
|
|
||||||
- [DesEcbEncrypt](#DesEcbEncrypt)
|
- [DesEcbEncrypt](#DesEcbEncrypt)
|
||||||
- [DesEcbDecrypt](#DesEcbDecrypt)
|
- [DesEcbDecrypt](#DesEcbDecrypt)
|
||||||
- [DesCbcEncrypt](#DesCbcEncrypt)
|
- [DesCbcEncrypt](#DesCbcEncrypt)
|
||||||
@@ -43,7 +42,6 @@ import (
|
|||||||
- [DesCfbDecrypt](#DesCfbDecrypt)
|
- [DesCfbDecrypt](#DesCfbDecrypt)
|
||||||
- [DesOfbEncrypt](#DesOfbEncrypt)
|
- [DesOfbEncrypt](#DesOfbEncrypt)
|
||||||
- [DesOfbDecrypt](#DesOfbDecrypt)
|
- [DesOfbDecrypt](#DesOfbDecrypt)
|
||||||
|
|
||||||
- [HmacMd5](#HmacMd5)
|
- [HmacMd5](#HmacMd5)
|
||||||
- [HmacSha1](#HmacSha1)
|
- [HmacSha1](#HmacSha1)
|
||||||
- [HmacSha256](#HmacSha256)
|
- [HmacSha256](#HmacSha256)
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import (
|
|||||||
- [IsEmpty](#IsEmpty)
|
- [IsEmpty](#IsEmpty)
|
||||||
- [Union](#Union)
|
- [Union](#Union)
|
||||||
- [Intersection](#Intersection)
|
- [Intersection](#Intersection)
|
||||||
|
|
||||||
- [SymmetricDifference](#SymmetricDifference)
|
- [SymmetricDifference](#SymmetricDifference)
|
||||||
- [Minus](#Minus)
|
- [Minus](#Minus)
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import (
|
|||||||
- [IsEmpty](#IsEmpty)
|
- [IsEmpty](#IsEmpty)
|
||||||
- [Union](#Union)
|
- [Union](#Union)
|
||||||
- [Intersection](#Intersection)
|
- [Intersection](#Intersection)
|
||||||
|
|
||||||
- [SymmetricDifference](#SymmetricDifference)
|
- [SymmetricDifference](#SymmetricDifference)
|
||||||
- [Minus](#Minus)
|
- [Minus](#Minus)
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import (
|
|||||||
- [PreOrderTraverse](#BSTree_PreOrderTraverse)
|
- [PreOrderTraverse](#BSTree_PreOrderTraverse)
|
||||||
- [InOrderTraverse](#BSTree_InOrderTraverse)
|
- [InOrderTraverse](#BSTree_InOrderTraverse)
|
||||||
- [PostOrderTraverse](#BSTree_PostOrderTraverse)
|
- [PostOrderTraverse](#BSTree_PostOrderTraverse)
|
||||||
|
|
||||||
- [LevelOrderTraverse](#BSTree_LevelOrderTraverse)
|
- [LevelOrderTraverse](#BSTree_LevelOrderTraverse)
|
||||||
- [Depth](#BSTree_Depth)
|
- [Depth](#BSTree_Depth)
|
||||||
- [HasSubTree](#BSTree_HasSubTree)
|
- [HasSubTree](#BSTree_HasSubTree)
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import (
|
|||||||
- [PreOrderTraverse](#BSTree_PreOrderTraverse)
|
- [PreOrderTraverse](#BSTree_PreOrderTraverse)
|
||||||
- [InOrderTraverse](#BSTree_InOrderTraverse)
|
- [InOrderTraverse](#BSTree_InOrderTraverse)
|
||||||
- [PostOrderTraverse](#BSTree_PostOrderTraverse)
|
- [PostOrderTraverse](#BSTree_PostOrderTraverse)
|
||||||
|
|
||||||
- [LevelOrderTraverse](#BSTree_LevelOrderTraverse)
|
- [LevelOrderTraverse](#BSTree_LevelOrderTraverse)
|
||||||
- [Depth](#BSTree_Depth)
|
- [Depth](#BSTree_Depth)
|
||||||
- [HasSubTree](#BSTree_HasSubTree)
|
- [HasSubTree](#BSTree_HasSubTree)
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ import (
|
|||||||
- [GetNightTimestamp](#GetNightTimestamp)
|
- [GetNightTimestamp](#GetNightTimestamp)
|
||||||
- [FormatTimeToStr](#FormatTimeToStr)
|
- [FormatTimeToStr](#FormatTimeToStr)
|
||||||
- [FormatStrToTime](#FormatStrToTime)
|
- [FormatStrToTime](#FormatStrToTime)
|
||||||
|
|
||||||
- [NewUnixNow](#NewUnixNow)
|
- [NewUnixNow](#NewUnixNow)
|
||||||
- [NewUnix](#NewUnix)
|
- [NewUnix](#NewUnix)
|
||||||
- [NewFormat](#NewFormat)
|
- [NewFormat](#NewFormat)
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import (
|
|||||||
- [GetNightTimestamp](#GetNightTimestamp)
|
- [GetNightTimestamp](#GetNightTimestamp)
|
||||||
- [FormatTimeToStr](#FormatTimeToStr)
|
- [FormatTimeToStr](#FormatTimeToStr)
|
||||||
- [FormatStrToTime](#FormatStrToTime)
|
- [FormatStrToTime](#FormatStrToTime)
|
||||||
|
|
||||||
- [NewUnixNow](#NewUnixNow)
|
- [NewUnixNow](#NewUnixNow)
|
||||||
- [NewUnix](#NewUnix)
|
- [NewUnix](#NewUnix)
|
||||||
- [NewFormat](#NewFormat)
|
- [NewFormat](#NewFormat)
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import (
|
|||||||
- [IsExist](#IsExist)
|
- [IsExist](#IsExist)
|
||||||
- [IsLink](#IsLink)
|
- [IsLink](#IsLink)
|
||||||
- [IsDir](#IsDir)
|
- [IsDir](#IsDir)
|
||||||
|
|
||||||
- [ListFileNames](#ListFileNames)
|
- [ListFileNames](#ListFileNames)
|
||||||
- [RemoveFile](#RemoveFile)
|
- [RemoveFile](#RemoveFile)
|
||||||
- [ReadFileToString](#ReadFileToString)
|
- [ReadFileToString](#ReadFileToString)
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import (
|
|||||||
- [IsExist](#IsExist)
|
- [IsExist](#IsExist)
|
||||||
- [IsLink](#IsLink)
|
- [IsLink](#IsLink)
|
||||||
- [IsDir](#IsDir)
|
- [IsDir](#IsDir)
|
||||||
|
|
||||||
- [ListFileNames](#ListFileNames)
|
- [ListFileNames](#ListFileNames)
|
||||||
- [RemoveFile](#RemoveFile)
|
- [RemoveFile](#RemoveFile)
|
||||||
- [ReadFileToString](#ReadFileToString)
|
- [ReadFileToString](#ReadFileToString)
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import (
|
|||||||
- [MaxBy](#MaxBy)
|
- [MaxBy](#MaxBy)
|
||||||
- [Min](#Min)
|
- [Min](#Min)
|
||||||
- [MinBy](#MaxBy)
|
- [MinBy](#MaxBy)
|
||||||
|
|
||||||
- [Percent](#Percent)
|
- [Percent](#Percent)
|
||||||
- [RoundToFloat](#RoundToFloat)
|
- [RoundToFloat](#RoundToFloat)
|
||||||
- [RoundToString](#RoundToString)
|
- [RoundToString](#RoundToString)
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import (
|
|||||||
- [MaxBy](#MaxBy)
|
- [MaxBy](#MaxBy)
|
||||||
- [Min](#Min)
|
- [Min](#Min)
|
||||||
- [MinBy](#MaxBy)
|
- [MinBy](#MaxBy)
|
||||||
|
|
||||||
- [Percent](#Percent)
|
- [Percent](#Percent)
|
||||||
- [RoundToFloat](#RoundToFloat)
|
- [RoundToFloat](#RoundToFloat)
|
||||||
- [RoundToString](#RoundToString)
|
- [RoundToString](#RoundToString)
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import (
|
|||||||
## Index
|
## Index
|
||||||
- [ConvertMapToQueryString](#ConvertMapToQueryString)
|
- [ConvertMapToQueryString](#ConvertMapToQueryString)
|
||||||
- [EncodeUrl](#EncodeUrl)
|
- [EncodeUrl](#EncodeUrl)
|
||||||
|
|
||||||
- [GetInternalIp](#GetInternalIp)
|
- [GetInternalIp](#GetInternalIp)
|
||||||
- [GetIps](#GetIps)
|
- [GetIps](#GetIps)
|
||||||
- [GetMacAddrs](#GetMacAddrs)
|
- [GetMacAddrs](#GetMacAddrs)
|
||||||
@@ -38,7 +37,6 @@ import (
|
|||||||
- [SendRequest](#SendRequest)
|
- [SendRequest](#SendRequest)
|
||||||
- [DecodeResponse](#DecodeResponse)
|
- [DecodeResponse](#DecodeResponse)
|
||||||
- [StructToUrlValues](#StructToUrlValues)
|
- [StructToUrlValues](#StructToUrlValues)
|
||||||
|
|
||||||
- [HttpGet<sup>Deprecated</sup>](#HttpGet)
|
- [HttpGet<sup>Deprecated</sup>](#HttpGet)
|
||||||
- [HttpDelete<sup>Deprecated</sup>](#HttpDelete)
|
- [HttpDelete<sup>Deprecated</sup>](#HttpDelete)
|
||||||
- [HttpPost<sup>Deprecated</sup>](#HttpPost)
|
- [HttpPost<sup>Deprecated</sup>](#HttpPost)
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import (
|
|||||||
- [GetMacAddrs](#GetMacAddrs)
|
- [GetMacAddrs](#GetMacAddrs)
|
||||||
- [GetPublicIpInfo](#GetPublicIpInfo)
|
- [GetPublicIpInfo](#GetPublicIpInfo)
|
||||||
- [GetRequestPublicIp](#GetRequestPublicIp)
|
- [GetRequestPublicIp](#GetRequestPublicIp)
|
||||||
|
|
||||||
- [IsPublicIP](#IsPublicIP)
|
- [IsPublicIP](#IsPublicIP)
|
||||||
- [IsInternalIP](#IsInternalIP)
|
- [IsInternalIP](#IsInternalIP)
|
||||||
- [HttpRequest](#HttpRequest)
|
- [HttpRequest](#HttpRequest)
|
||||||
@@ -38,13 +37,11 @@ import (
|
|||||||
- [SendRequest](#SendRequest)
|
- [SendRequest](#SendRequest)
|
||||||
- [DecodeResponse](#DecodeResponse)
|
- [DecodeResponse](#DecodeResponse)
|
||||||
- [StructToUrlValues](#StructToUrlValues)
|
- [StructToUrlValues](#StructToUrlValues)
|
||||||
|
|
||||||
- [HttpGet<sup>Deprecated</sup>](#HttpGet)
|
- [HttpGet<sup>Deprecated</sup>](#HttpGet)
|
||||||
- [HttpDelete<sup>Deprecated</sup>](#HttpDelete)
|
- [HttpDelete<sup>Deprecated</sup>](#HttpDelete)
|
||||||
- [HttpPost<sup>Deprecated</sup>](#HttpPost)
|
- [HttpPost<sup>Deprecated</sup>](#HttpPost)
|
||||||
- [HttpPut<sup>Deprecated</sup>](#HttpPut)
|
- [HttpPut<sup>Deprecated</sup>](#HttpPut)
|
||||||
- [HttpPatch<sup>Deprecated</sup>](#HttpPatch)
|
- [HttpPatch<sup>Deprecated</sup>](#HttpPatch)
|
||||||
|
|
||||||
- [ParseHttpResponse](#ParseHttpResponse)
|
- [ParseHttpResponse](#ParseHttpResponse)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import (
|
|||||||
- [RandInt](#RandInt)
|
- [RandInt](#RandInt)
|
||||||
- [RandString](#RandString)
|
- [RandString](#RandString)
|
||||||
- [RandUpper](#RandUpper)
|
- [RandUpper](#RandUpper)
|
||||||
|
|
||||||
- [RandLower](#RandLower)
|
- [RandLower](#RandLower)
|
||||||
- [RandNumeral](#RandNumeral)
|
- [RandNumeral](#RandNumeral)
|
||||||
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import (
|
|||||||
- [RandInt](#RandInt)
|
- [RandInt](#RandInt)
|
||||||
- [RandString](#RandString)
|
- [RandString](#RandString)
|
||||||
- [RandUpper](#RandUpper)
|
- [RandUpper](#RandUpper)
|
||||||
|
|
||||||
- [RandLower](#RandLower)
|
- [RandLower](#RandLower)
|
||||||
- [RandNumeral](#RandNumeral)
|
- [RandNumeral](#RandNumeral)
|
||||||
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
||||||
|
|||||||
@@ -172,8 +172,8 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
arr := []string{"a", "b", "c", "d", "e"}
|
arr := []string{"a", "b", "c", "d", "e"}
|
||||||
res := slice.Chunk(InterfaceSlice(arr), 3)
|
res := slice.Chunk((arr), 3)
|
||||||
fmt.Println(res) //[][]any{{"a", "b", "c"}, {"d", "e"}}
|
fmt.Println(res) //[][]string{{"a", "b", "c"}, {"d", "e"}}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ import (
|
|||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
|
|
||||||
|
|
||||||
- [AppendIfAbsent](#AppendIfAbsent)
|
- [AppendIfAbsent](#AppendIfAbsent)
|
||||||
- [Contain](#Contain)
|
- [Contain](#Contain)
|
||||||
- [ContainSubSlice](#ContainSubSlice)
|
- [ContainSubSlice](#ContainSubSlice)
|
||||||
@@ -46,7 +48,6 @@ import (
|
|||||||
- [GroupBy](#GroupBy)
|
- [GroupBy](#GroupBy)
|
||||||
- [GroupWith](#GroupWith)
|
- [GroupWith](#GroupWith)
|
||||||
- [IntSlice](#IntSlice)
|
- [IntSlice](#IntSlice)
|
||||||
|
|
||||||
- [InterfaceSlice](#InterfaceSlice)
|
- [InterfaceSlice](#InterfaceSlice)
|
||||||
- [Intersection](#Intersection)
|
- [Intersection](#Intersection)
|
||||||
- [InsertAt](#InsertAt)
|
- [InsertAt](#InsertAt)
|
||||||
@@ -72,6 +73,7 @@ import (
|
|||||||
- [Without](#Without)
|
- [Without](#Without)
|
||||||
- [KeyBy](#KeyBy)
|
- [KeyBy](#KeyBy)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
@@ -172,8 +174,8 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
arr := []string{"a", "b", "c", "d", "e"}
|
arr := []string{"a", "b", "c", "d", "e"}
|
||||||
res := slice.Chunk(InterfaceSlice(arr), 3)
|
res := slice.Chunk(arr, 3)
|
||||||
fmt.Println(res) //[][]any{{"a", "b", "c"}, {"d", "e"}}
|
fmt.Println(res) //[][]string{{"a", "b", "c"}, {"d", "e"}}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import (
|
|||||||
- [SnakeCase](#SnakeCase)
|
- [SnakeCase](#SnakeCase)
|
||||||
- [SplitEx](#SplitEx)
|
- [SplitEx](#SplitEx)
|
||||||
- [Wrap](#Wrap)
|
- [Wrap](#Wrap)
|
||||||
|
|
||||||
- [Unwrap](#Unwrap)
|
- [Unwrap](#Unwrap)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import (
|
|||||||
- [SnakeCase](#SnakeCase)
|
- [SnakeCase](#SnakeCase)
|
||||||
- [SplitEx](#SplitEx)
|
- [SplitEx](#SplitEx)
|
||||||
- [Wrap](#Wrap)
|
- [Wrap](#Wrap)
|
||||||
|
|
||||||
- [Unwrap](#Unwrap)
|
- [Unwrap](#Unwrap)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import (
|
|||||||
- [IsCreditCard](#IsCreditCard)
|
- [IsCreditCard](#IsCreditCard)
|
||||||
- [IsDns](#IsDns)
|
- [IsDns](#IsDns)
|
||||||
- [IsEmail](#IsEmail)
|
- [IsEmail](#IsEmail)
|
||||||
|
|
||||||
- [IsEmptyString](#IsEmptyString)
|
- [IsEmptyString](#IsEmptyString)
|
||||||
- [IsFloatStr](#IsFloatStr)
|
- [IsFloatStr](#IsFloatStr)
|
||||||
- [IsNumberStr](#IsNumberStr)
|
- [IsNumberStr](#IsNumberStr)
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import (
|
|||||||
- [IsCreditCard](#IsCreditCard)
|
- [IsCreditCard](#IsCreditCard)
|
||||||
- [IsDns](#IsDns)
|
- [IsDns](#IsDns)
|
||||||
- [IsEmail](#IsEmail)
|
- [IsEmail](#IsEmail)
|
||||||
|
|
||||||
- [IsEmptyString](#IsEmptyString)
|
- [IsEmptyString](#IsEmptyString)
|
||||||
- [IsFloatStr](#IsFloatStr)
|
- [IsFloatStr](#IsFloatStr)
|
||||||
- [IsNumberStr](#IsNumberStr)
|
- [IsNumberStr](#IsNumberStr)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -213,6 +214,7 @@ func Zip(fpath string, destPath string) error {
|
|||||||
|
|
||||||
// UnZip unzip the file and save it to destPath
|
// UnZip unzip the file and save it to destPath
|
||||||
func UnZip(zipFile string, destPath string) error {
|
func UnZip(zipFile string, destPath string) error {
|
||||||
|
|
||||||
zipReader, err := zip.OpenReader(zipFile)
|
zipReader, err := zip.OpenReader(zipFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -220,7 +222,12 @@ func UnZip(zipFile string, destPath string) error {
|
|||||||
defer zipReader.Close()
|
defer zipReader.Close()
|
||||||
|
|
||||||
for _, f := range zipReader.File {
|
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() {
|
if f.FileInfo().IsDir() {
|
||||||
os.MkdirAll(path, os.ModePerm)
|
os.MkdirAll(path, os.ModePerm)
|
||||||
} else {
|
} else {
|
||||||
@@ -249,6 +256,17 @@ func UnZip(zipFile string, destPath string) error {
|
|||||||
return nil
|
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
|
// IsLink checks if a file is symbol link or not
|
||||||
func IsLink(path string) bool {
|
func IsLink(path string) bool {
|
||||||
fi, err := os.Lstat(path)
|
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()
|
||||||
|
}
|
||||||
@@ -42,29 +42,18 @@ func Chunk[T any](slice []T, size int) [][]T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
length := len(slice)
|
for _, item := range slice {
|
||||||
if size == 1 || size >= length {
|
l := len(result)
|
||||||
for _, v := range slice {
|
if l == 0 || len(result[l-1]) == size {
|
||||||
var tmp []T
|
result = append(result, []T{})
|
||||||
tmp = append(tmp, v)
|
l++
|
||||||
result = append(result, tmp)
|
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// divide slice equally
|
result[l-1] = append(result[l-1], item)
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey
|
// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey
|
||||||
@@ -181,33 +170,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.
|
// 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 {
|
func Every[T any](slice []T, predicate func(index int, item T) bool) bool {
|
||||||
var currentLength int
|
|
||||||
|
|
||||||
for i, v := range slice {
|
for i, v := range slice {
|
||||||
if predicate(i, v) {
|
if !predicate(i, v) {
|
||||||
currentLength++
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentLength == len(slice)
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// None return true if all the values in the slice mismatch the criteria
|
// 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 {
|
func None[T any](slice []T, predicate func(index int, item T) bool) bool {
|
||||||
|
l := 0
|
||||||
var currentLength int
|
|
||||||
for i, v := range slice {
|
for i, v := range slice {
|
||||||
if !predicate(i, v) {
|
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.
|
// 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 {
|
func Some[T any](slice []T, predicate func(index int, item T) bool) bool {
|
||||||
|
|
||||||
for i, v := range slice {
|
for i, v := range slice {
|
||||||
if predicate(i, v) {
|
if predicate(i, v) {
|
||||||
return true
|
return true
|
||||||
@@ -432,6 +417,15 @@ func ReplaceAll[T comparable](slice []T, old T, new T) []T {
|
|||||||
return Replace(slice, old, new, -1)
|
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.
|
// InterfaceSlice convert param to slice of interface.
|
||||||
func InterfaceSlice(slice any) []any {
|
func InterfaceSlice(slice any) []any {
|
||||||
sv := sliceValue(slice)
|
sv := sliceValue(slice)
|
||||||
@@ -629,6 +623,17 @@ func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T {
|
|||||||
return result
|
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.
|
// Intersection creates a slice of unique values that included by all slices.
|
||||||
func Intersection[T comparable](slices ...[]T) []T {
|
func Intersection[T comparable](slices ...[]T) []T {
|
||||||
if len(slices) == 0 {
|
if len(slices) == 0 {
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ func TestChunk(t *testing.T) {
|
|||||||
|
|
||||||
arr := []string{"a", "b", "c", "d", "e"}
|
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"}}
|
r1 := [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
|
||||||
assert.Equal(r1, Chunk(arr, 1))
|
assert.Equal(r1, Chunk(arr, 1))
|
||||||
|
|
||||||
@@ -43,8 +47,11 @@ func TestChunk(t *testing.T) {
|
|||||||
r4 := [][]string{{"a", "b", "c", "d"}, {"e"}}
|
r4 := [][]string{{"a", "b", "c", "d"}, {"e"}}
|
||||||
assert.Equal(r4, Chunk(arr, 4))
|
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))
|
assert.Equal(r5, Chunk(arr, 5))
|
||||||
|
|
||||||
|
r6 := [][]string{{"a", "b", "c", "d", "e"}}
|
||||||
|
assert.Equal(r6, Chunk(arr, 6))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompact(t *testing.T) {
|
func TestCompact(t *testing.T) {
|
||||||
@@ -439,6 +446,18 @@ func TestUnionBy(t *testing.T) {
|
|||||||
assert.Equal(result, []int{0, 2, 4, 10})
|
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) {
|
func TestIntersection(t *testing.T) {
|
||||||
s1 := []int{1, 2, 2, 3}
|
s1 := []int{1, 2, 2, 3}
|
||||||
s2 := []int{1, 2, 3, 4}
|
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"})
|
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