1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-15 02:02:27 +08:00

Compare commits

...

141 Commits

Author SHA1 Message Date
dudaodong
ca2a51b37e test&doc: add example and update doc for function package 2023-01-06 17:12:32 +08:00
dudaodong
be444f521d test: add examples for function package 2023-01-06 16:12:44 +08:00
dudaodong
e21dd07d46 doc: update doc for validator package 2023-01-06 14:14:00 +08:00
dudaodong
4044deac70 test: add examples for validator package 2023-01-06 11:31:51 +08:00
dudaodong
d9c6294775 fix: fix goline error 2023-01-05 14:55:47 +08:00
dudaodong
d8505d1a5f doc: update doc for slice package 2023-01-05 14:49:30 +08:00
dudaodong
6498c7d68a test: add examples for slice package 2023-01-05 11:26:56 +08:00
dudaodong
5f0211f0c4 test: add examples for slice package 2023-01-04 14:44:10 +08:00
dudaodong
1de2e2cedd test: add examples for random package 2023-01-03 15:10:42 +08:00
dudaodong
c8f8b1b7d9 doc: update doc for fileutil package 2023-01-03 14:33:29 +08:00
dudaodong
3062eb7789 test: add examples for fileutil package 2023-01-03 11:55:23 +08:00
dudaodong
64d5486cc6 doc: add example and update doc for formatter package 2023-01-02 15:35:09 +08:00
dudaodong
6d57891f66 doc: update document for mathutil package 2023-01-02 15:19:47 +08:00
dudaodong
927245e47f test: add example for mathutil package 2023-01-02 14:59:49 +08:00
dudaodong
31fdbee0b5 doc: update document for maputil function 2023-01-02 14:21:23 +08:00
dudaodong
3712819994 test: add exmaple for maputil package 2023-01-02 14:06:12 +08:00
dudaodong
6d7dec1cea doc: update doc for convertor package 2023-01-01 22:00:23 +08:00
dudaodong
9b6a004dbc test: add example for convertor package 2023-01-01 21:24:24 +08:00
dudaodong
3ad6f4bd9e doc: update doc for condition package 2022-12-31 16:47:18 +08:00
dudaodong
b8c6746f31 test: add example for condition package 2022-12-31 16:26:43 +08:00
dudaodong
0bf8bbf4cb doc: add example and update docment for channel 2022-12-31 13:38:52 +08:00
dudaodong
a6ba1028c5 doc: add example and update docment for channel 2022-12-31 13:35:44 +08:00
dudaodong
cc54dd7ec9 doc: add example and update docment for channel 2022-12-31 13:12:16 +08:00
dudaodong
54834dba4c doc: update doc for system package 2022-12-30 14:28:49 +08:00
dudaodong
e996d4c945 test: add examples for system package 2022-12-30 14:14:37 +08:00
dudaodong
ae92ae7666 test: add examples for system package 2022-12-30 14:14:22 +08:00
dudaodong
526e568d0e doc: add doc for Substring 2022-12-29 20:03:59 +08:00
dudaodong
1dc5e8ac23 feat: add Substring function 2022-12-29 19:55:40 +08:00
dudaodong
b5f7b0e670 doc: update document for algorithm package 2022-12-29 15:32:47 +08:00
dudaodong
39c576248c test: add examples for lrucache 2022-12-29 11:43:14 +08:00
dudaodong
eb164d1536 test: add examples for lrucache function 2022-12-29 11:43:04 +08:00
dudaodong
68e170080c test: add examples for sort function 2022-12-28 20:18:05 +08:00
dudaodong
652b09135c test: add examples for search function 2022-12-28 17:58:56 +08:00
dudaodong
bff24c89bc doc: update strutil document 2022-12-27 17:26:34 +08:00
dudaodong
49a460eef8 doc: update document and add playgound example for strutil package 2022-12-27 16:57:32 +08:00
dudaodong
a58e52e53c test: add example function for strutil package 2022-12-27 15:40:51 +08:00
dudaodong
b07356423f fix: update format 2022-12-26 17:57:06 +08:00
dudaodong
005dd9d2ab fix: fix word misspelling 2022-12-26 17:51:30 +08:00
dudaodong
65315dafb1 feat: add take iterator 2022-12-26 17:20:14 +08:00
dudaodong
b06fb6736d feat: add reduce for iterator 2022-12-26 16:55:24 +08:00
dudaodong
b9f0854950 feat: add join iterator 2022-12-26 15:58:44 +08:00
dudaodong
6a2dd328ad feat: add test for map iterator and filter iterator 2022-12-26 15:13:35 +08:00
dudaodong
dd1147f6d0 feat: add Channel iterator 2022-12-26 15:02:28 +08:00
dudaodong
6da7ce64af test: add unit test for RangeIterator 2022-12-26 14:24:23 +08:00
dudaodong
b7e5d946f1 release v2.1.12 2022-12-15 16:55:20 +08:00
dudaodong
687db4ce79 update readme file 2022-12-15 16:44:30 +08:00
dudaodong
a9a4bb8841 update readme file 2022-12-15 16:42:22 +08:00
dudaodong
bc6cb5f61b fix code issue in doc 2022-12-15 16:38:50 +08:00
dudaodong
2c57266f8e test: update some test functions 2022-12-15 15:22:01 +08:00
dudaodong
57e49c9520 doc: update document for funcations CamelCase/KebabCase/UpperKebabCase/SnakeCase/UpperSnakeCase 2022-12-14 21:42:43 +08:00
dudaodong
985c9a5d9a refactor: refact CamelCase function 2022-12-14 21:27:29 +08:00
dudaodong
5cfb11f036 feat: fix and add SnakeCase/UpperSnakeCase 2022-12-14 21:17:30 +08:00
dudaodong
5b3d48a1e7 feat: fix and add KebabCase/UpperKebabCase 2022-12-14 21:09:22 +08:00
dudaodong
d0576e028f clean code 2022-12-13 19:52:37 +08:00
dudaodong
76bdec2b54 test: add cases for Capitalize 2022-12-13 16:16:16 +08:00
dudaodong
fa20aba3a7 fix: fix CamelCase function bug 2022-12-13 14:23:32 +08:00
dudaodong
7a4a429e23 test: add cases for Capitalize 2022-12-12 21:11:19 +08:00
dudaodong
a70ec6ad1e refactor: use constraints from golang.org/x/exp/constraints 2022-12-11 11:25:34 +08:00
dudaodong
e435fa271b doc: update doc for Comma 2022-12-10 21:03:16 +08:00
dudaodong
1533d00891 refactor: update Comma function 2022-12-10 20:59:27 +08:00
dudaodong
8b99641de0 test: remove print info in test function 2022-12-10 19:19:49 +08:00
dudaodong
251f899f18 fix: TestExecCommand failed in linux/macos 2022-12-10 19:12:19 +08:00
dudaodong
00407e5182 fix: fix lint issue 2022-12-10 19:09:18 +08:00
dudaodong
4e457ad672 change: change update and insert function in link datastruture 2022-12-10 17:45:29 +08:00
dudaodong
7d8d9c3543 fix: fix lint issue 2022-12-10 16:55:06 +08:00
dudaodong
1197e8d1b6 fix: fix lint issue 2022-12-10 16:53:41 +08:00
dudaodong
13bbe19ab2 fix: fix lint issue 2022-12-10 16:41:40 +08:00
dudaodong
2725575d2f doc: add doc for IsGBK' 2022-12-09 19:28:45 +08:00
dudaodong
037d2729ce doc: update doc for ExecCommand 2022-12-09 19:20:15 +08:00
dudaodong
09d98745b0 fix: fix ExecCommand bug in windows 2022-12-09 19:10:55 +08:00
dudaodong
af5cfe6da1 feat: add IsGBK validator 2022-12-09 17:36:59 +08:00
Mickls
d59259bbe0 feat: A more reasonable IndexOf function (#66) 2022-12-09 11:31:40 +08:00
dudaodong
3189628d54 fix: fix AesCfbDecrypt 2022-12-08 15:07:40 +08:00
dudaodong
62c5e251a5 remove website 2022-12-08 14:55:21 +08:00
dudaodong
6e6444c8c0 refator: clean code 2022-12-06 20:20:36 +08:00
dudaodong
dd613e98b2 refator: clean code 2022-12-06 20:00:41 +08:00
dudaodong
2d905ab03e fix: fix word misspelling 2022-12-04 11:25:02 +08:00
dudaodong
205fedb197 release v2.1.11 2022-12-04 11:11:05 +08:00
dudaodong
4c864da62d doc: update readme file 2022-12-04 11:10:19 +08:00
dudaodong
263ab7e316 clean code 2022-12-03 14:41:58 +08:00
dudaodong
809b7a53df doc: update docment for slice package 2022-12-03 14:33:15 +08:00
dudaodong
61c43daabb doc: add doc for Count and CountBy function 2022-12-03 14:20:37 +08:00
dudaodong
18914ee2cd feat: add deprecat IntSlice, InterfaceSlice and StringSlice 2022-12-03 14:00:44 +08:00
dudaodong
0a8058956f feat: add Count and CountBy function 2022-12-03 13:04:12 +08:00
dudaodong
a044da7d2f refactor: clean code 2022-12-03 12:58:25 +08:00
dudaodong
f8b785c4cb doc: add doc for Sort and SortBy 2022-12-02 15:47:16 +08:00
dudaodong
82c8a04c35 make SortByField function deprecate 2022-12-02 15:17:08 +08:00
dudaodong
280ecb5cef feat: add SortBy function for slice 2022-12-02 14:53:57 +08:00
dudaodong
ec27ad4c40 feat: add Sort function for slice 2022-12-01 22:46:56 +08:00
dudaodong
d66f92cd68 doc: update slice and convertor document 2022-12-01 11:43:10 +08:00
dudaodong
d8ed692651 refactor: code improvement, ToString, Contain, Compact 2022-12-01 11:38:25 +08:00
dudaodong
a16de97d1d refactor: code improvement, ToString, Contain, Compact 2022-12-01 11:38:14 +08:00
dudaodong
6f458e4367 doc: add doc for NewSetFromSlice 2022-11-30 12:34:56 +08:00
dudaodong
37c7508ad0 feat: add NewSetFromSlice function for set 2022-11-30 12:00:55 +08:00
dudaodong
acb5844b15 docs: add doc for AddIfNotExist and AddIfNotExistBy 2022-11-30 11:54:04 +08:00
dudaodong
76f4eeea16 docs: add doc for Merge and Repeat 2022-11-30 11:35:32 +08:00
dudaodong
5466a23019 refactor: fix bad code smell 2022-11-30 11:20:13 +08:00
dudaodong
5692982dd1 feat: add AddIfNotExistBy function for set 2022-11-29 23:59:22 +08:00
dudaodong
c39c8914fb refactor: fix bad code smell 2022-11-29 23:50:17 +08:00
dudaodong
29bdca1bd2 feat: add AddIfNotExist function for set 2022-11-29 23:39:29 +08:00
dudaodong
eb66d038ac feat: update iterator package 2022-11-29 13:58:48 +08:00
dudaodong
a99ada5ee1 feat: add iterator package 2022-11-28 19:24:25 +08:00
dudaodong
a87faf5453 feat: add Repeate function for slice 2022-11-27 21:43:38 +08:00
dudaodong
ab6fec2f69 refactor: fix bad code smell 2022-11-27 21:36:30 +08:00
dudaodong
7b290989f5 feat: add Merge function 2022-11-27 20:20:00 +08:00
dudaodong
5722c724e6 Merge branch 'main' into v2 2022-11-27 19:29:33 +08:00
dudaodong
f84584ca04 doc: add website link 2022-11-27 18:44:25 +08:00
dudaodong
80cbbdc787 docs: format index content 2022-11-26 17:37:09 +08:00
dudaodong
be148e07ba fix: fix chunk slice bug 2022-11-26 16:21:57 +08:00
dudaodong
d36ab5cc3a fix: go report issue ineffassign 2022-11-18 17:05:54 +08:00
dudaodong
2b17329094 release v2.1.10 2022-11-17 16:11:48 +08:00
dudaodong
f869a0a670 fix: issue#62: fix ZipSlip bug 2022-11-16 16:04:38 +08:00
dudaodong
be000a4bd6 fix: issue#62: fix ZipSlip bug 2022-11-16 15:08:42 +08:00
DuDaoDong
81efa800ea Create SECURITY.md 2022-11-16 11:31:50 +08:00
dudaodong
a783de57a8 release v2.1.9 2022-11-08 15:22:37 +08:00
dudaodong
a622959a78 docs: add new functions readme 2022-11-08 15:21:31 +08:00
dudaodong
f709dd53ce docs: update doc for random package 2022-11-08 14:48:02 +08:00
dudaodong
ee9e9625e2 docs: add doc for UnionBy and KeyBy 2022-11-08 14:21:17 +08:00
dudaodong
84da7d4f27 feat: add RandUpper, RandLower, RandNumeral, and RandNumeralOrLetter 2022-11-06 20:04:52 +08:00
dudaodong
089fd4e13c feat: add UnionBy for slice 2022-11-05 19:45:34 +08:00
dudaodong
6c40e02324 refactor: clean code 2022-11-05 19:33:56 +08:00
dudaodong
a270b1b634 feat: make code clear 2022-11-05 17:09:56 +08:00
dudaodong
260fb795d3 feat: add KeyBy for slice 2022-11-05 16:52:31 +08:00
dudaodong
8c036f830c refactor: remove unuseful code 2022-11-05 16:39:42 +08:00
dudaodong
bf332b9f1c release v2.1.8 2022-10-21 16:22:52 +08:00
dudaodong
6b6cd66f9f Merge branch 'main' into v2 2022-10-19 17:31:51 +08:00
燕归来
5399c2290e feat: support type alias for Number constraint (#60) 2022-10-19 17:21:53 +08:00
dudaodong
6248293c49 doc: add docment for Replace and ReplaceAll 2022-10-17 10:40:35 +08:00
dudaodong
eced25b76d test: add unit test for Replace and ReplaceAll 2022-10-17 10:26:54 +08:00
dudaodong
96a4327aa7 fix: fix some static check issues 2022-10-15 15:03:24 +08:00
dudaodong
fcfbdea597 feat: add Replace and ReplaceAll for slice 2022-10-15 14:49:49 +08:00
dudaodong
87896f917a doc: add document for pipeline 2022-10-15 12:36:11 +08:00
dudaodong
b8563ed646 feat: add Pipeline function 2022-10-15 12:29:47 +08:00
dudaodong
1ccf0af2b3 doc: update document for datetime package 2022-10-14 10:20:38 +08:00
dudaodong
6314889c6a release v2.1.7 2022-10-14 10:08:43 +08:00
dudaodong
294bd5a5ed doc: update document for hashmap 2022-10-11 15:41:09 +08:00
dudaodong
ca44815fd5 Merge branch 'main' into v2 2022-10-11 15:02:28 +08:00
dudaodong
bcd1cabf80 feat: add Keys and Values function for hashmap 2022-10-10 16:34:05 +08:00
CyJaySong
4edefcca67 expand BeginOfWeek、EndOfWeek (#59) 2022-10-10 15:44:22 +08:00
dudaodong
fab24c8d12 feat: add Iterate for hashmap datastructure 2022-10-10 15:25:30 +08:00
dudaodong
604acd9b07 doc:add lancet api doc website 2022-09-29 18:41:22 +08:00
130 changed files with 10148 additions and 2593 deletions

1046
README.md

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

15
SECURITY.md Normal file
View 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.

View File

@@ -26,7 +26,7 @@ type LRUCache[K comparable, V any] struct {
length int length int
} }
// NewLRUCache return a LRUCache pointer // NewLRUCache creates a LRUCache pointer instance.
func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] { func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] {
return &LRUCache[K, V]{ return &LRUCache[K, V]{
cache: make(map[K]*lruNode[K, V], capacity), cache: make(map[K]*lruNode[K, V], capacity),
@@ -37,7 +37,8 @@ func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] {
} }
} }
// Get value of key from lru cache // Get value of key from lru cache.
// Play: https://go.dev/play/p/iUynEfOP8G0
func (l *LRUCache[K, V]) Get(key K) (V, bool) { func (l *LRUCache[K, V]) Get(key K) (V, bool) {
var value V var value V
@@ -50,7 +51,8 @@ func (l *LRUCache[K, V]) Get(key K) (V, bool) {
return value, false return value, false
} }
// Put value of key into lru cache // Put value of key into lru cache.
// Play: https://go.dev/play/p/iUynEfOP8G0
func (l *LRUCache[K, V]) Put(key K, value V) { func (l *LRUCache[K, V]) Put(key K, value V) {
node, ok := l.cache[key] node, ok := l.cache[key]
if !ok { if !ok {
@@ -69,6 +71,23 @@ func (l *LRUCache[K, V]) Put(key K, value V) {
l.length = len(l.cache) l.length = len(l.cache)
} }
// Delete item from lru cache.
func (l *LRUCache[K, V]) Delete(key K) bool {
node, ok := l.cache[key]
if ok {
key := l.deleteNode(node)
delete(l.cache, key)
return true
}
return false
}
// Len returns the number of items in the cache.
func (l *LRUCache[K, V]) Len() int {
return l.length
}
func (l *LRUCache[K, V]) addNode(node *lruNode[K, V]) { func (l *LRUCache[K, V]) addNode(node *lruNode[K, V]) {
if l.tail != nil { if l.tail != nil {
l.tail.next = node l.tail.next = node

View File

@@ -0,0 +1,79 @@
package algorithm
import "fmt"
func ExampleLRUCache_Put() {
cache := NewLRUCache[int, int](2)
cache.Put(1, 1)
cache.Put(2, 2)
result1, ok1 := cache.Get(1)
result2, ok2 := cache.Get(2)
result3, ok3 := cache.Get(3)
fmt.Println(result1, ok1)
fmt.Println(result2, ok2)
fmt.Println(result3, ok3)
// Output:
// 1 true
// 2 true
// 0 false
}
func ExampleLRUCache_Get() {
cache := NewLRUCache[int, int](2)
cache.Put(1, 1)
cache.Put(2, 2)
result1, ok1 := cache.Get(1)
result2, ok2 := cache.Get(2)
result3, ok3 := cache.Get(3)
fmt.Println(result1, ok1)
fmt.Println(result2, ok2)
fmt.Println(result3, ok3)
// Output:
// 1 true
// 2 true
// 0 false
}
func ExampleLRUCache_Delete() {
cache := NewLRUCache[int, int](2)
cache.Put(1, 1)
cache.Put(2, 2)
result1, ok1 := cache.Get(1)
ok2 := cache.Delete(2)
_, ok3 := cache.Get(2)
fmt.Println(result1, ok1)
fmt.Println(ok2)
fmt.Println(ok3)
// Output:
// 1 true
// true
// false
}
func ExampleLRUCache_Len() {
cache := NewLRUCache[int, int](2)
cache.Put(1, 1)
cache.Put(2, 2)
result := cache.Len()
fmt.Println(result)
// Output:
// 2
}

View File

@@ -9,13 +9,13 @@ import (
func TestLRUCache(t *testing.T) { func TestLRUCache(t *testing.T) {
asssert := internal.NewAssert(t, "TestLRUCache") asssert := internal.NewAssert(t, "TestLRUCache")
cache := NewLRUCache[int, int](2) cache := NewLRUCache[int, int](3)
cache.Put(1, 1) cache.Put(1, 1)
cache.Put(2, 2) cache.Put(2, 2)
cache.Put(3, 3)
_, ok := cache.Get(0) asssert.Equal(3, cache.Len())
asssert.Equal(false, ok)
v, ok := cache.Get(1) v, ok := cache.Get(1)
asssert.Equal(true, ok) asssert.Equal(true, ok)
@@ -25,12 +25,9 @@ func TestLRUCache(t *testing.T) {
asssert.Equal(true, ok) asssert.Equal(true, ok)
asssert.Equal(2, v) asssert.Equal(2, v)
cache.Put(3, 3) ok = cache.Delete(2)
v, ok = cache.Get(1)
asssert.Equal(false, ok)
asssert.NotEqual(1, v)
v, ok = cache.Get(3)
asssert.Equal(true, ok) asssert.Equal(true, ok)
asssert.Equal(3, v)
_, ok = cache.Get(2)
asssert.Equal(false, ok)
} }

View File

@@ -1,26 +1,27 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved. // Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license // Use of this source code is governed by MIT license
// Package algorithm contain some basic algorithm functions. eg. sort, search, list, linklist, stack, queue, tree, graph. TODO // Package algorithm contain some basic algorithm functions. eg. sort, search, list, linklist, stack, queue, tree, graph.
package algorithm package algorithm
import "github.com/duke-git/lancet/v2/lancetconstraints" import "github.com/duke-git/lancet/v2/lancetconstraints"
// Search algorithms see https://github.com/TheAlgorithms/Go/tree/master/search // Search algorithms see https://github.com/TheAlgorithms/Go/tree/master/search
// LinearSearch Simple linear search algorithm that iterates over all elements of an slice // LinearSearch return the index of target in slice base on equal function.
// If a target is found, the index of the target is returned. Else the function return -1 // If not found return -1
func LinearSearch[T any](slice []T, target T, comparator lancetconstraints.Comparator) int { func LinearSearch[T any](slice []T, target T, equal func(a, b T) bool) int {
for i, v := range slice { for i, v := range slice {
if comparator.Compare(v, target) == 0 { if equal(v, target) {
return i return i
} }
} }
return -1 return -1
} }
// BinarySearch search for target within a sorted slice, recursive call itself. // BinarySearch return the index of target within a sorted slice, use binary search (recursive call itself).
// If a target is found, the index of the target is returned. Else the function return -1 // If not found return -1.
// Play: https://go.dev/play/p/t6MeGiUSN47
func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int { func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int {
if highIndex < lowIndex || len(sortedSlice) == 0 { if highIndex < lowIndex || len(sortedSlice) == 0 {
return -1 return -1
@@ -39,8 +40,9 @@ func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, com
return midIndex return midIndex
} }
// BinaryIterativeSearch search for target within a sorted slice. // BinaryIterativeSearch return the index of target within a sorted slice, use binary search (no recursive).
// If a target is found, the index of the target is returned. Else the function return -1 // If not found return -1.
// Play: https://go.dev/play/p/Anozfr8ZLH3
func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int { func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int {
startIndex := lowIndex startIndex := lowIndex
endIndex := highIndex endIndex := highIndex

View File

@@ -0,0 +1,51 @@
package algorithm
import "fmt"
func ExampleLinearSearch() {
numbers := []int{3, 4, 5, 3, 2, 1}
equalFunc := func(a, b int) bool {
return a == b
}
result1 := LinearSearch(numbers, 3, equalFunc)
result2 := LinearSearch(numbers, 6, equalFunc)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 0
// -1
}
func ExampleBinarySearch() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
comparator := &intComparator{}
result1 := BinarySearch(numbers, 5, 0, len(numbers)-1, comparator)
result2 := BinarySearch(numbers, 9, 0, len(numbers)-1, comparator)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 4
// -1
}
func ExampleBinaryIterativeSearch() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
comparator := &intComparator{}
result1 := BinaryIterativeSearch(numbers, 5, 0, len(numbers)-1, comparator)
result2 := BinaryIterativeSearch(numbers, 9, 0, len(numbers)-1, comparator)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 4
// -1
}

View File

@@ -6,20 +6,24 @@ import (
"github.com/duke-git/lancet/v2/internal" "github.com/duke-git/lancet/v2/internal"
) )
var sortedNumbers = []int{1, 2, 3, 4, 5, 6, 7, 8}
func TestLinearSearch(t *testing.T) { func TestLinearSearch(t *testing.T) {
asssert := internal.NewAssert(t, "TestLinearSearch") asssert := internal.NewAssert(t, "TestLinearSearch")
comparator := &intComparator{} numbers := []int{3, 4, 5, 3, 2, 1}
asssert.Equal(4, LinearSearch(sortedNumbers, 5, comparator)) equalFunc := func(a, b int) bool {
asssert.Equal(-1, LinearSearch(sortedNumbers, 9, comparator)) return a == b
}
asssert.Equal(0, LinearSearch(numbers, 3, equalFunc))
asssert.Equal(-1, LinearSearch(numbers, 6, equalFunc))
} }
func TestBinarySearch(t *testing.T) { func TestBinarySearch(t *testing.T) {
asssert := internal.NewAssert(t, "TestBinarySearch") asssert := internal.NewAssert(t, "TestBinarySearch")
sortedNumbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
comparator := &intComparator{} comparator := &intComparator{}
asssert.Equal(4, BinarySearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator)) asssert.Equal(4, BinarySearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator))
asssert.Equal(-1, BinarySearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator)) asssert.Equal(-1, BinarySearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator))
} }
@@ -27,7 +31,9 @@ func TestBinarySearch(t *testing.T) {
func TestBinaryIterativeSearch(t *testing.T) { func TestBinaryIterativeSearch(t *testing.T) {
asssert := internal.NewAssert(t, "TestBinaryIterativeSearch") asssert := internal.NewAssert(t, "TestBinaryIterativeSearch")
sortedNumbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
comparator := &intComparator{} comparator := &intComparator{}
asssert.Equal(4, BinaryIterativeSearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator)) asssert.Equal(4, BinaryIterativeSearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator))
asssert.Equal(-1, BinaryIterativeSearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator)) asssert.Equal(-1, BinaryIterativeSearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator))
} }

View File

@@ -1,12 +1,12 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved. // Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license // Use of this source code is governed by MIT license
// Package algorithm contain some basic algorithm functions. eg. sort, search
package algorithm package algorithm
import "github.com/duke-git/lancet/v2/lancetconstraints" import "github.com/duke-git/lancet/v2/lancetconstraints"
// BubbleSort use bubble to sort slice. // BubbleSort applys the bubble sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/GNdv7Jg2Taj
func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator) { func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator) {
for i := 0; i < len(slice); i++ { for i := 0; i < len(slice); i++ {
for j := 0; j < len(slice)-1-i; j++ { for j := 0; j < len(slice)-1-i; j++ {
@@ -18,7 +18,8 @@ func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator) {
} }
} }
// InsertionSort use insertion to sort slice. // InsertionSort applys the insertion sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/G5LJiWgJJW6
func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator) { func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
for i := 0; i < len(slice); i++ { for i := 0; i < len(slice); i++ {
for j := i; j > 0; j-- { for j := i; j > 0; j-- {
@@ -32,7 +33,8 @@ func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
} }
} }
// SelectionSort use selection to sort slice. // SelectionSort applys the selection sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/oXovbkekayS
func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) { func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
for i := 0; i < len(slice); i++ { for i := 0; i < len(slice); i++ {
min := i min := i
@@ -45,7 +47,8 @@ func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
} }
} }
// ShellSort shell sort slice. // ShellSort applys the shell sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/3ibkszpJEu3
func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) { func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) {
size := len(slice) size := len(slice)
@@ -64,7 +67,8 @@ func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) {
} }
} }
// QuickSort quick sorting for slice, lowIndex is 0 and highIndex is len(slice)-1 // QuickSort quick sorting for slice, lowIndex is 0 and highIndex is len(slice)-1.
// Play: https://go.dev/play/p/7Y7c1Elk3ax
func QuickSort[T any](slice []T, comparator lancetconstraints.Comparator) { func QuickSort[T any](slice []T, comparator lancetconstraints.Comparator) {
quickSort(slice, 0, len(slice)-1, comparator) quickSort(slice, 0, len(slice)-1, comparator)
} }
@@ -93,7 +97,8 @@ func partition[T any](slice []T, lowIndex, highIndex int, comparator lancetconst
return i return i
} }
// HeapSort use heap to sort slice // HeapSort applys the heap sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/u6Iwa1VZS_f
func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator) { func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator) {
size := len(slice) size := len(slice)
@@ -126,7 +131,8 @@ func sift[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraint
slice[i] = temp slice[i] = temp
} }
// MergeSort merge sorting for slice // MergeSort applys the merge sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/ydinn9YzUJn
func MergeSort[T any](slice []T, comparator lancetconstraints.Comparator) { func MergeSort[T any](slice []T, comparator lancetconstraints.Comparator) {
mergeSort(slice, 0, len(slice)-1, comparator) mergeSort(slice, 0, len(slice)-1, comparator)
} }
@@ -167,7 +173,8 @@ func merge[T any](slice []T, lowIndex, midIndex, highIndex int, comparator lance
} }
} }
// CountSort use count sorting for slice // CountSort applys the count sort algorithm to sort the collection, don't change the original collection data.
// Play: https://go.dev/play/p/tB-Umgm0DrP
func CountSort[T any](slice []T, comparator lancetconstraints.Comparator) []T { func CountSort[T any](slice []T, comparator lancetconstraints.Comparator) []T {
size := len(slice) size := len(slice)
out := make([]T, size) out := make([]T, size)

View File

@@ -0,0 +1,93 @@
package algorithm
import "fmt"
func ExampleBubbleSort() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
BubbleSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
func ExampleCountSort() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
sortedNumber := CountSort(numbers, comparator)
fmt.Println(numbers)
fmt.Println(sortedNumber)
// Output:
// [2 1 5 3 6 4]
// [1 2 3 4 5 6]
}
func ExampleHeapSort() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
HeapSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
func ExampleMergeSort() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
MergeSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
func ExampleInsertionSort() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
InsertionSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
func ExampleSelectionSort() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
SelectionSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
func ExampleShellSort() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
ShellSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
func ExampleQuickSort() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
QuickSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}

View File

@@ -66,6 +66,7 @@ func TestBubbleSortForStructSlice(t *testing.T) {
func TestBubbleSortForIntSlice(t *testing.T) { func TestBubbleSortForIntSlice(t *testing.T) {
asssert := internal.NewAssert(t, "TestBubbleSortForIntSlice") asssert := internal.NewAssert(t, "TestBubbleSortForIntSlice")
numbers := []int{2, 1, 5, 3, 6, 4} numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
BubbleSort(numbers, comparator) BubbleSort(numbers, comparator)

View File

@@ -11,17 +11,18 @@ import (
// Channel is a logic object which can generate or manipulate go channel // Channel is a logic object which can generate or manipulate go channel
// all methods of Channel are in the book tilted《Concurrency in Go》 // all methods of Channel are in the book tilted《Concurrency in Go》
type Channel struct { type Channel[T any] struct {
} }
// NewChannel return a Channel instance // NewChannel return a Channel instance
func NewChannel() *Channel { func NewChannel[T any]() *Channel[T] {
return &Channel{} return &Channel[T]{}
} }
// Generate a data of type any chan, put param `values` into the chan // Generate creates channel, then put values into the channel.
func (c *Channel) Generate(ctx context.Context, values ...any) <-chan any { // Play:
dataStream := make(chan any) func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T {
dataStream := make(chan T)
go func() { go func() {
defer close(dataStream) defer close(dataStream)
@@ -38,9 +39,10 @@ func (c *Channel) Generate(ctx context.Context, values ...any) <-chan any {
return dataStream return dataStream
} }
// Repeat return a data of type any chan, put param `values` into the chan repeatly until cancel the context. // Repeat create channel, put values into the channel repeatly until cancel the context.
func (c *Channel) Repeat(ctx context.Context, values ...any) <-chan any { // Play:
dataStream := make(chan any) func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T {
dataStream := make(chan T)
go func() { go func() {
defer close(dataStream) defer close(dataStream)
@@ -57,10 +59,11 @@ func (c *Channel) Repeat(ctx context.Context, values ...any) <-chan any {
return dataStream return dataStream
} }
// RepeatFn return a chan, excutes fn repeatly, and put the result into retruned chan // RepeatFn create a channel, excutes fn repeatly, and put the result into the channel
// until close the `done` channel // until close context.
func (c *Channel) RepeatFn(ctx context.Context, fn func() any) <-chan any { // Play:
dataStream := make(chan any) func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T {
dataStream := make(chan T)
go func() { go func() {
defer close(dataStream) defer close(dataStream)
@@ -75,9 +78,10 @@ func (c *Channel) RepeatFn(ctx context.Context, fn func() any) <-chan any {
return dataStream return dataStream
} }
// Take return a chan whose values are tahken from another chan // Take create a channel whose values are taken from another channel with limit number.
func (c *Channel) Take(ctx context.Context, valueStream <-chan any, number int) <-chan any { // Play:
takeStream := make(chan any) func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T {
takeStream := make(chan T)
go func() { go func() {
defer close(takeStream) defer close(takeStream)
@@ -94,16 +98,17 @@ func (c *Channel) Take(ctx context.Context, valueStream <-chan any, number int)
return takeStream return takeStream
} }
// FanIn merge multiple channels into one channel // FanIn merge multiple channels into one channel.
func (c *Channel) FanIn(ctx context.Context, channels ...<-chan any) <-chan any { // Play:
out := make(chan any) func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T {
out := make(chan T)
go func() { go func() {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(channels)) wg.Add(len(channels))
for _, c := range channels { for _, c := range channels {
go func(c <-chan any) { go func(c <-chan T) {
defer wg.Done() defer wg.Done()
for v := range c { for v := range c {
select { select {
@@ -121,10 +126,11 @@ func (c *Channel) FanIn(ctx context.Context, channels ...<-chan any) <-chan any
return out return out
} }
// Tee split one chanel into two channels // Tee split one chanel into two channels, until cancel the context.
func (c *Channel) Tee(ctx context.Context, in <-chan any) (<-chan any, <-chan any) { // Play:
out1 := make(chan any) func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T) {
out2 := make(chan any) out1 := make(chan T)
out2 := make(chan T)
go func() { go func() {
defer close(out1) defer close(out1)
@@ -147,18 +153,19 @@ func (c *Channel) Tee(ctx context.Context, in <-chan any) (<-chan any, <-chan an
return out1, out2 return out1, out2
} }
// Bridge link multiply channels into one channel // Bridge link multiply channels into one channel.
func (c *Channel) Bridge(ctx context.Context, chanStream <-chan <-chan any) <-chan any { // Play:
valStream := make(chan any) func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T {
valStream := make(chan T)
go func() { go func() {
defer close(valStream) defer close(valStream)
for { for {
var stream <-chan any var stream <-chan T
select { select {
case maybeStream, ok := <-chanStream: case maybeStream, ok := <-chanStream:
if ok == false { if !ok {
return return
} }
stream = maybeStream stream = maybeStream
@@ -178,8 +185,9 @@ func (c *Channel) Bridge(ctx context.Context, chanStream <-chan <-chan any) <-ch
return valStream return valStream
} }
// Or read one or more channels into one channel, will close when any readin channel is closed // Or read one or more channels into one channel, will close when any readin channel is closed.
func (c *Channel) Or(channels ...<-chan any) <-chan any { // Play:
func (c *Channel[T]) Or(channels ...<-chan T) <-chan T {
switch len(channels) { switch len(channels) {
case 0: case 0:
return nil return nil
@@ -187,7 +195,7 @@ func (c *Channel) Or(channels ...<-chan any) <-chan any {
return channels[0] return channels[0]
} }
orDone := make(chan any) orDone := make(chan T)
go func() { go func() {
defer close(orDone) defer close(orDone)
@@ -199,17 +207,12 @@ func (c *Channel) Or(channels ...<-chan any) <-chan any {
case <-channels[1]: case <-channels[1]:
} }
default: default:
m := len(channels) / 2
select { select {
case <-c.Or(channels[:m]...): case <-channels[0]:
case <-c.Or(channels[m:]...): case <-channels[1]:
case <-channels[2]:
case <-c.Or(append(channels[3:], orDone)...):
} }
// select {
// case <-channels[0]:
// case <-channels[1]:
// case <-channels[2]:
// case <-c.Or(append(channels[3:], orDone)...):
// }
} }
}() }()
@@ -217,8 +220,9 @@ func (c *Channel) Or(channels ...<-chan any) <-chan any {
} }
// OrDone read a channel into another channel, will close until cancel context. // OrDone read a channel into another channel, will close until cancel context.
func (c *Channel) OrDone(ctx context.Context, channel <-chan any) <-chan any { // Play:
valStream := make(chan any) func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T {
valStream := make(chan T)
go func() { go func() {
defer close(valStream) defer close(valStream)

View File

@@ -0,0 +1,196 @@
package concurrency
import (
"context"
"fmt"
"time"
)
func ExampleChannel_Generate() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
intStream := c.Generate(ctx, 1, 2, 3)
fmt.Println(<-intStream)
fmt.Println(<-intStream)
fmt.Println(<-intStream)
// Output:
// 1
// 2
// 3
}
func ExampleChannel_Repeat() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4)
for v := range intStream {
fmt.Println(v)
}
// Output:
// 1
// 2
// 1
// 2
}
func ExampleChannel_RepeatFn() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fn := func() string {
return "hello"
}
c := NewChannel[string]()
intStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
for v := range intStream {
fmt.Println(v)
}
// Output:
// hello
// hello
// hello
}
func ExampleChannel_Take() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
numbers := make(chan int, 5)
numbers <- 1
numbers <- 2
numbers <- 3
numbers <- 4
numbers <- 5
defer close(numbers)
c := NewChannel[int]()
intStream := c.Take(ctx, numbers, 3)
for v := range intStream {
fmt.Println(v)
}
// Output:
// 1
// 2
// 3
}
func ExampleChannel_FanIn() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
channels := make([]<-chan int, 2)
for i := 0; i < 2; i++ {
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 2)
}
chs := c.FanIn(ctx, channels...)
for v := range chs {
fmt.Println(v) //1 1 0 0 or 0 0 1 1
}
}
func ExampleChannel_Or() {
sig := func(after time.Duration) <-chan any {
c := make(chan any)
go func() {
defer close(c)
time.Sleep(after)
}()
return c
}
start := time.Now()
c := NewChannel[any]()
<-c.Or(
sig(1*time.Second),
sig(2*time.Second),
sig(3*time.Second),
)
if time.Since(start).Seconds() < 2 {
fmt.Println("ok")
}
// Output:
// ok
}
func ExampleChannel_OrDone() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
for v := range c.OrDone(ctx, intStream) {
fmt.Println(v)
}
// Output:
// 1
// 1
// 1
}
func ExampleChannel_Tee() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 2)
ch1, ch2 := c.Tee(ctx, intStream)
for v := range ch1 {
fmt.Println(v)
fmt.Println(<-ch2)
}
// Output:
// 1
// 1
// 1
// 1
}
func ExampleChannel_Bridge() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
genVals := func() <-chan <-chan int {
out := make(chan (<-chan int))
go func() {
defer close(out)
for i := 1; i <= 5; i++ {
stream := make(chan int, 1)
stream <- i
close(stream)
out <- stream
}
}()
return out
}
for v := range c.Bridge(ctx, genVals()) {
fmt.Println(v)
}
// Output:
// 1
// 2
// 3
// 4
// 5
}

View File

@@ -14,12 +14,9 @@ func TestGenerate(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := NewChannel() c := NewChannel[int]()
intStream := c.Generate(ctx, 1, 2, 3) intStream := c.Generate(ctx, 1, 2, 3)
// for v := range intStream {
// t.Log(v) //1, 2, 3
// }
assert.Equal(1, <-intStream) assert.Equal(1, <-intStream)
assert.Equal(2, <-intStream) assert.Equal(2, <-intStream)
assert.Equal(3, <-intStream) assert.Equal(3, <-intStream)
@@ -31,12 +28,9 @@ func TestRepeat(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := NewChannel() c := NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 5) intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 5)
// for v := range intStream {
// t.Log(v) //1, 2, 1, 2, 1
// }
assert.Equal(1, <-intStream) assert.Equal(1, <-intStream)
assert.Equal(2, <-intStream) assert.Equal(2, <-intStream)
assert.Equal(1, <-intStream) assert.Equal(1, <-intStream)
@@ -50,17 +44,13 @@ func TestRepeatFn(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
fn := func() any { fn := func() string {
s := "a" s := "a"
return s return s
} }
c := NewChannel() c := NewChannel[string]()
dataStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3) dataStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
// for v := range dataStream {
// t.Log(v) //a, a, a
// }
assert.Equal("a", <-dataStream) assert.Equal("a", <-dataStream)
assert.Equal("a", <-dataStream) assert.Equal("a", <-dataStream)
assert.Equal("a", <-dataStream) assert.Equal("a", <-dataStream)
@@ -72,7 +62,7 @@ func TestTake(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
numbers := make(chan any, 5) numbers := make(chan int, 5)
numbers <- 1 numbers <- 1
numbers <- 2 numbers <- 2
numbers <- 3 numbers <- 3
@@ -80,7 +70,7 @@ func TestTake(t *testing.T) {
numbers <- 5 numbers <- 5
defer close(numbers) defer close(numbers)
c := NewChannel() c := NewChannel[int]()
intStream := c.Take(ctx, numbers, 3) intStream := c.Take(ctx, numbers, 3)
assert.Equal(1, <-intStream) assert.Equal(1, <-intStream)
@@ -94,8 +84,8 @@ func TestFanIn(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := NewChannel() c := NewChannel[int]()
channels := make([]<-chan any, 3) channels := make([]<-chan int, 3)
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 3) channels[i] = c.Take(ctx, c.Repeat(ctx, i), 3)
@@ -124,7 +114,7 @@ func TestOr(t *testing.T) {
start := time.Now() start := time.Now()
c := NewChannel() c := NewChannel[any]()
<-c.Or( <-c.Or(
sig(1*time.Second), sig(1*time.Second),
sig(2*time.Second), sig(2*time.Second),
@@ -133,9 +123,7 @@ func TestOr(t *testing.T) {
sig(5*time.Second), sig(5*time.Second),
) )
t.Logf("done after %v", time.Since(start)) assert.Equal(true, time.Since(start).Seconds() < 2)
assert.Equal(1, 1)
} }
func TestOrDone(t *testing.T) { func TestOrDone(t *testing.T) {
@@ -144,16 +132,12 @@ func TestOrDone(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := NewChannel() c := NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3) intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
var res any
for val := range c.OrDone(ctx, intStream) { for val := range c.OrDone(ctx, intStream) {
t.Logf("%v", val) assert.Equal(1, val)
res = val
} }
assert.Equal(1, res)
} }
func TestTee(t *testing.T) { func TestTee(t *testing.T) {
@@ -162,15 +146,13 @@ func TestTee(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := NewChannel() c := NewChannel[int]()
inStream := c.Take(ctx, c.Repeat(ctx, 1), 4) inStream := c.Take(ctx, c.Repeat(ctx, 1), 4)
out1, out2 := c.Tee(ctx, inStream) out1, out2 := c.Tee(ctx, inStream)
for val := range out1 { for val := range out1 {
val1 := val val1 := val
val2 := <-out2 val2 := <-out2
// t.Log("val1 is", val1)
// t.Log("val2 is", val2)
assert.Equal(1, val1) assert.Equal(1, val1)
assert.Equal(1, val2) assert.Equal(1, val2)
} }
@@ -182,13 +164,13 @@ func TestBridge(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := NewChannel() c := NewChannel[int]()
genVals := func() <-chan <-chan any { genVals := func() <-chan <-chan int {
chanStream := make(chan (<-chan any)) chanStream := make(chan (<-chan int))
go func() { go func() {
defer close(chanStream) defer close(chanStream)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
stream := make(chan any, 1) stream := make(chan int, 1)
stream <- i stream <- i
close(stream) close(stream)
chanStream <- stream chanStream <- stream

View File

@@ -13,6 +13,7 @@ import "reflect"
// If the type has an IsZero() bool method, the opposite value is returned. // If the type has an IsZero() bool method, the opposite value is returned.
// Slices and maps are truthy if they have a length greater than zero. // Slices and maps are truthy if they have a length greater than zero.
// All other types are truthy if they are not their zero value. // All other types are truthy if they are not their zero value.
// Play: https://go.dev/play/p/ETzeDJRSvhm
func Bool[T any](value T) bool { func Bool[T any](value T) bool {
switch m := any(value).(type) { switch m := any(value).(type) {
case interface{ Bool() bool }: case interface{ Bool() bool }:
@@ -34,40 +35,47 @@ func reflectValue(vp any) bool {
} }
// And returns true if both a and b are truthy. // And returns true if both a and b are truthy.
// Play: https://go.dev/play/p/W1SSUmt6pvr
func And[T, U any](a T, b U) bool { func And[T, U any](a T, b U) bool {
return Bool(a) && Bool(b) return Bool(a) && Bool(b)
} }
// Or returns false iff neither a nor b is truthy. // Or returns false if neither a nor b is truthy.
// Play: https://go.dev/play/p/UlQTxHaeEkq
func Or[T, U any](a T, b U) bool { func Or[T, U any](a T, b U) bool {
return Bool(a) || Bool(b) return Bool(a) || Bool(b)
} }
// Xor returns true iff a or b but not both is truthy. // Xor returns true if a or b but not both is truthy.
// Play: https://go.dev/play/p/gObZrW7ZbG8
func Xor[T, U any](a T, b U) bool { func Xor[T, U any](a T, b U) bool {
valA := Bool(a) valA := Bool(a)
valB := Bool(b) valB := Bool(b)
return (valA || valB) && valA != valB return (valA || valB) && valA != valB
} }
// Nor returns true iff neither a nor b is truthy. // Nor returns true if neither a nor b is truthy.
// Play: https://go.dev/play/p/g2j08F_zZky
func Nor[T, U any](a T, b U) bool { func Nor[T, U any](a T, b U) bool {
return !(Bool(a) || Bool(b)) return !(Bool(a) || Bool(b))
} }
// Xnor returns true iff both a and b or neither a nor b are truthy. // Xnor returns true if both a and b or neither a nor b are truthy.
// Play: https://go.dev/play/p/OuDB9g51643
func Xnor[T, U any](a T, b U) bool { func Xnor[T, U any](a T, b U) bool {
valA := Bool(a) valA := Bool(a)
valB := Bool(b) valB := Bool(b)
return (valA && valB) || (!valA && !valB) return (valA && valB) || (!valA && !valB)
} }
// Nand returns false iff both a and b are truthy. // Nand returns false if both a and b are truthy.
// Play: https://go.dev/play/p/vSRMLxLIbq8
func Nand[T, U any](a T, b U) bool { func Nand[T, U any](a T, b U) bool {
return !Bool(a) || !Bool(b) return !Bool(a) || !Bool(b)
} }
// TernaryOperator checks the value of param `isTrue`, if true return ifValue else return elseValue // TernaryOperator checks the value of param `isTrue`, if true return ifValue else return elseValue.
// Play: https://go.dev/play/p/ElllPZY0guT
func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U { func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U {
if Bool(isTrue) { if Bool(isTrue) {
return ifValue return ifValue

View File

@@ -0,0 +1,163 @@
package condition
import "fmt"
func ExampleBool() {
// bool
result1 := Bool(false)
result2 := Bool(true)
fmt.Println(result1)
fmt.Println(result2)
// integer
result3 := Bool(0)
result4 := Bool(1)
fmt.Println(result3)
fmt.Println(result4)
// string
result5 := Bool("")
result6 := Bool(" ")
fmt.Println(result5)
fmt.Println(result6)
// slice
var nums = []int{}
result7 := Bool(nums)
nums = append(nums, 1, 2)
result8 := Bool(nums)
fmt.Println(result7)
fmt.Println(result8)
// Output:
// false
// true
// false
// true
// false
// true
// false
// true
}
func ExampleAnd() {
result1 := And(0, 1)
result2 := And(0, "")
result3 := And(0, "0")
result4 := And(1, "0")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// false
// false
// false
// true
}
func ExampleOr() {
result1 := Or(0, "")
result2 := Or(0, 1)
result3 := Or(0, "0")
result4 := Or(1, "0")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// false
// true
// true
// true
}
func ExampleXor() {
result1 := Xor(0, 0)
result2 := Xor(1, 1)
result3 := Xor(0, 1)
result4 := Xor(1, 0)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// false
// false
// true
// true
}
func ExampleNor() {
result1 := Nor(0, 0)
result2 := Nor(1, 1)
result3 := Nor(0, 1)
result4 := Nor(1, 0)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// false
// false
// false
}
func ExampleXnor() {
result1 := Xnor(0, 0)
result2 := Xnor(1, 1)
result3 := Xnor(0, 1)
result4 := Xnor(1, 0)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// false
// false
}
func ExampleNand() {
result1 := Nand(0, 0)
result2 := Nand(1, 0)
result3 := Nand(0, 1)
result4 := Nand(1, 1)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// true
// false
}
func ExampleTernaryOperator() {
conditionTrue := 2 > 1
result1 := TernaryOperator(conditionTrue, 0, 1)
fmt.Println(result1)
conditionFalse := 2 > 3
result2 := TernaryOperator(conditionFalse, 0, 1)
fmt.Println(result2)
// Output:
// 0
// 1
}

View File

@@ -17,12 +17,14 @@ import (
"strings" "strings"
) )
// ToBool convert string to a boolean // ToBool convert string to boolean.
// Play: https://go.dev/play/p/ARht2WnGdIN
func ToBool(s string) (bool, error) { func ToBool(s string) (bool, error) {
return strconv.ParseBool(s) return strconv.ParseBool(s)
} }
// ToBytes convert interface to bytes // ToBytes convert value to byte slice.
// Play: https://go.dev/play/p/fAMXYFDvOvr
func ToBytes(value any) ([]byte, error) { func ToBytes(value any) ([]byte, error) {
v := reflect.ValueOf(value) v := reflect.ValueOf(value)
@@ -63,7 +65,8 @@ func ToBytes(value any) ([]byte, error) {
} }
} }
// ToChar convert string to char slice // ToChar convert string to char slice.
// Play: https://go.dev/play/p/JJ1SvbFkVdM
func ToChar(s string) []string { func ToChar(s string) []string {
c := make([]string, 0) c := make([]string, 0)
if len(s) == 0 { if len(s) == 0 {
@@ -75,7 +78,8 @@ func ToChar(s string) []string {
return c return c
} }
// ToChannel convert a array of elements to a read-only channels // ToChannel convert a slice of elements to a read-only channel.
// Play: https://go.dev/play/p/hOx_oYZbAnL
func ToChannel[T any](array []T) <-chan T { func ToChannel[T any](array []T) <-chan T {
ch := make(chan T) ch := make(chan T)
@@ -90,38 +94,59 @@ func ToChannel[T any](array []T) <-chan T {
} }
// ToString convert value to string // ToString convert value to string
// for number, string, []byte, will convert to string
// for other type (slice, map, array, struct) will call json.Marshal.
// Play: https://go.dev/play/p/nF1zOOslpQq
func ToString(value any) string { func ToString(value any) string {
result := ""
if value == nil { if value == nil {
return result return ""
} }
v := reflect.ValueOf(value) switch val := value.(type) {
case float32:
switch value.(type) { return strconv.FormatFloat(float64(val), 'f', -1, 32)
case float32, float64: case float64:
result = strconv.FormatFloat(v.Float(), 'f', -1, 64) return strconv.FormatFloat(val, 'f', -1, 64)
return result case int:
case int, int8, int16, int32, int64: return strconv.FormatInt(int64(val), 10)
result = strconv.FormatInt(v.Int(), 10) case int8:
return result return strconv.FormatInt(int64(val), 10)
case uint, uint8, uint16, uint32, uint64: case int16:
result = strconv.FormatUint(v.Uint(), 10) return strconv.FormatInt(int64(val), 10)
return result case int32:
return strconv.FormatInt(int64(val), 10)
case int64:
return strconv.FormatInt(val, 10)
case uint:
return strconv.FormatUint(uint64(val), 10)
case uint8:
return strconv.FormatUint(uint64(val), 10)
case uint16:
return strconv.FormatUint(uint64(val), 10)
case uint32:
return strconv.FormatUint(uint64(val), 10)
case uint64:
return strconv.FormatUint(val, 10)
case string: case string:
result = v.String() return val
return result
case []byte: case []byte:
result = string(v.Bytes()) return string(val)
return result
default: default:
newValue, _ := json.Marshal(value) b, err := json.Marshal(val)
result = string(newValue) if err != nil {
return result return ""
}
return string(b)
// todo: maybe we should't supprt other type conversion
// v := reflect.ValueOf(value)
// log.Panicf("Unsupported data type: %s ", v.String())
// return ""
} }
} }
// ToJson convert value to a valid json string // ToJson convert value to a json string.
// Play: https://go.dev/play/p/2rLIkMmXWvR
func ToJson(value any) (string, error) { func ToJson(value any) (string, error) {
result, err := json.Marshal(value) result, err := json.Marshal(value)
if err != nil { if err != nil {
@@ -131,7 +156,8 @@ func ToJson(value any) (string, error) {
return string(result), nil return string(result), nil
} }
// ToFloat convert value to a float64, if input is not a float return 0.0 and error // ToFloat convert value to float64, if input is not a float return 0.0 and error.
// Play: https://go.dev/play/p/4YTmPCibqHJ
func ToFloat(value any) (float64, error) { func ToFloat(value any) (float64, error) {
v := reflect.ValueOf(value) v := reflect.ValueOf(value)
@@ -158,12 +184,13 @@ func ToFloat(value any) (float64, error) {
} }
} }
// ToInt convert value to a int64, if input is not a numeric format return 0 and error // ToInt convert value to int64 value, if input is not numerical, return 0 and error.
// Play: https://go.dev/play/p/9_h9vIt-QZ_b
func ToInt(value any) (int64, error) { func ToInt(value any) (int64, error) {
v := reflect.ValueOf(value) v := reflect.ValueOf(value)
var result int64 var result int64
err := fmt.Errorf("ToInt: invalid interface type %T", value) err := fmt.Errorf("ToInt: invalid value type %T", value)
switch value.(type) { switch value.(type) {
case int, int8, int16, int32, int64: case int, int8, int16, int32, int64:
result = v.Int() result = v.Int()
@@ -185,12 +212,14 @@ func ToInt(value any) (int64, error) {
} }
} }
// ToPointer returns a pointer to this value // ToPointer returns a pointer to passed value.
// Play: https://go.dev/play/p/ASf_etHNlw1
func ToPointer[T any](value T) *T { func ToPointer[T any](value T) *T {
return &value return &value
} }
// ToMap convert a slice or an array of structs to a map based on iteratee function // ToMap convert a slice of structs to a map based on iteratee function.
// Play: https://go.dev/play/p/tVFy7E-t24l
func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V { func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V {
result := make(map[K]V, len(array)) result := make(map[K]V, len(array))
for _, item := range array { for _, item := range array {
@@ -202,7 +231,8 @@ func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K
} }
// StructToMap convert struct to map, only convert exported struct field // StructToMap convert struct to map, only convert exported struct field
// map key is specified same as struct field tag `json` value // map key is specified same as struct field tag `json` value.
// Play: https://go.dev/play/p/KYGYJqNUBOI
func StructToMap(value any) (map[string]any, error) { func StructToMap(value any) (map[string]any, error) {
v := reflect.ValueOf(value) v := reflect.ValueOf(value)
t := reflect.TypeOf(value) t := reflect.TypeOf(value)
@@ -231,7 +261,8 @@ func StructToMap(value any) (map[string]any, error) {
return result, nil return result, nil
} }
// MapToSlice convert a map to a slice based on iteratee function // MapToSlice convert map to slice based on iteratee function.
// Play: https://go.dev/play/p/dmX4Ix5V6Wl
func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T { func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T {
result := make([]T, 0, len(aMap)) result := make([]T, 0, len(aMap))
@@ -242,7 +273,8 @@ func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T)
return result return result
} }
// ColorHexToRGB convert hex color to rgb color // ColorHexToRGB convert hex color to rgb color.
// Play: https://go.dev/play/p/o7_ft-JCJBV
func ColorHexToRGB(colorHex string) (red, green, blue int) { func ColorHexToRGB(colorHex string) (red, green, blue int) {
colorHex = strings.TrimPrefix(colorHex, "#") colorHex = strings.TrimPrefix(colorHex, "#")
color64, err := strconv.ParseInt(colorHex, 16, 32) color64, err := strconv.ParseInt(colorHex, 16, 32)
@@ -253,7 +285,8 @@ func ColorHexToRGB(colorHex string) (red, green, blue int) {
return color >> 16, (color & 0x00FF00) >> 8, color & 0x0000FF return color >> 16, (color & 0x00FF00) >> 8, color & 0x0000FF
} }
// ColorRGBToHex convert rgb color to hex color // ColorRGBToHex convert rgb color to hex color.
// Play: https://go.dev/play/p/nzKS2Ro87J1
func ColorRGBToHex(red, green, blue int) string { func ColorRGBToHex(red, green, blue int) string {
r := strconv.FormatInt(int64(red), 16) r := strconv.FormatInt(int64(red), 16)
g := strconv.FormatInt(int64(green), 16) g := strconv.FormatInt(int64(green), 16)
@@ -272,7 +305,8 @@ func ColorRGBToHex(red, green, blue int) string {
return "#" + r + g + b return "#" + r + g + b
} }
// EncodeByte encode data to byte // EncodeByte encode data to byte slice.
// Play: https://go.dev/play/p/DVmM1G5JfuP
func EncodeByte(data any) ([]byte, error) { func EncodeByte(data any) ([]byte, error) {
buffer := bytes.NewBuffer(nil) buffer := bytes.NewBuffer(nil)
encoder := gob.NewEncoder(buffer) encoder := gob.NewEncoder(buffer)
@@ -283,7 +317,8 @@ func EncodeByte(data any) ([]byte, error) {
return buffer.Bytes(), nil return buffer.Bytes(), nil
} }
// DecodeByte decode byte data to target object // DecodeByte decode byte slice data to target object.
// Play: https://go.dev/play/p/zI6xsmuQRbn
func DecodeByte(data []byte, target any) error { func DecodeByte(data []byte, target any) error {
buffer := bytes.NewBuffer(data) buffer := bytes.NewBuffer(data)
decoder := gob.NewDecoder(buffer) decoder := gob.NewDecoder(buffer)

View File

@@ -0,0 +1,254 @@
package convertor
import (
"fmt"
"strconv"
)
func ExampleToBool() {
cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"}
for i := 0; i < len(cases); i++ {
result, _ := ToBool(cases[i])
fmt.Println(result)
}
// Output:
// true
// true
// true
// false
// false
// false
// false
// false
// false
}
func ExampleToBytes() {
result1, _ := ToBytes(1)
result2, _ := ToBytes("abc")
result3, _ := ToBytes(true)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// [0 0 0 0 0 0 0 1]
// [97 98 99]
// [116 114 117 101]
}
func ExampleToChar() {
result1 := ToChar("")
result2 := ToChar("abc")
result3 := ToChar("1 2#3")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// []
// [a b c]
// [1 2 # 3]
}
func ExampleToChannel() {
ch := ToChannel([]int{1, 2, 3})
result1 := <-ch
result2 := <-ch
result3 := <-ch
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 1
// 2
// 3
}
func ExampleToString() {
result1 := ToString("")
result2 := ToString(nil)
result3 := ToString(0)
result4 := ToString(1.23)
result5 := ToString(true)
result6 := ToString(false)
result7 := ToString([]int{1, 2, 3})
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
fmt.Println(result7)
// Output:
//
//
// 0
// 1.23
// true
// false
// [1,2,3]
}
func ExampleToJson() {
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result1, err := ToJson(aMap)
if err != nil {
fmt.Printf("%v", err)
}
fmt.Println(result1)
// Output:
// {"a":1,"b":2,"c":3}
}
func ExampleToFloat() {
result1, _ := ToFloat("")
result2, _ := ToFloat("abc")
result3, _ := ToFloat("-1")
result4, _ := ToFloat("-.11")
result5, _ := ToFloat("1.23e3")
result6, _ := ToFloat(true)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
// Output:
// 0
// 0
// -1
// -0.11
// 1230
// 0
}
func ExampleToInt() {
result1, _ := ToInt("123")
result2, _ := ToInt("-123")
result3, _ := ToInt(float64(12.3))
result4, _ := ToInt("abc")
result5, _ := ToInt(true)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
// Output:
// 123
// -123
// 12
// 0
// 0
}
func ExampleToPointer() {
result := ToPointer(123)
fmt.Println(*result)
// Output:
// 123
}
func ExampleToMap() {
type Message struct {
name string
code int
}
messages := []Message{
{name: "Hello", code: 100},
{name: "Hi", code: 101},
}
result := ToMap(messages, func(msg Message) (int, string) {
return msg.code, msg.name
})
fmt.Println(result)
// Output:
// map[100:Hello 101:Hi]
}
func ExampleStructToMap() {
type People struct {
Name string `json:"name"`
age int
}
p := People{
"test",
100,
}
pm, _ := StructToMap(p)
fmt.Println(pm)
// Output:
// map[name:test]
}
func ExampleMapToSlice() {
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result := MapToSlice(aMap, func(key string, value int) string {
return key + ":" + strconv.Itoa(value)
})
fmt.Println(result) //[]string{"a:1", "c:3", "b:2"} (random order)
}
func ExampleColorHexToRGB() {
colorHex := "#003366"
r, g, b := ColorHexToRGB(colorHex)
fmt.Println(r, g, b)
// Output:
// 0 51 102
}
func ExampleColorRGBToHex() {
r := 0
g := 51
b := 102
colorHex := ColorRGBToHex(r, g, b)
fmt.Println(colorHex)
// Output:
// #003366
}
func ExampleEncodeByte() {
byteData, _ := EncodeByte("abc")
fmt.Println(byteData)
// Output:
// [6 12 0 3 97 98 99]
}
func ExampleDecodeByte() {
var obj string
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
err := DecodeByte(byteData, &obj)
if err != nil {
return
}
fmt.Println(obj)
// Output:
// abc
}

View File

@@ -27,14 +27,9 @@ func TestToChannel(t *testing.T) {
assert := internal.NewAssert(t, "TestToChannel") assert := internal.NewAssert(t, "TestToChannel")
ch := ToChannel([]int{1, 2, 3}) ch := ToChannel([]int{1, 2, 3})
val1, _ := <-ch assert.Equal(1, <-ch)
assert.Equal(1, val1) assert.Equal(2, <-ch)
assert.Equal(3, <-ch)
val2, _ := <-ch
assert.Equal(2, val2)
val3, _ := <-ch
assert.Equal(3, val3)
_, ok := <-ch _, ok := <-ch
assert.Equal(false, ok) assert.Equal(false, ok)
@@ -137,9 +132,10 @@ func TestToString(t *testing.T) {
"", "", "", "",
"0", "1", "-1", "0", "1", "-1",
"123", "123", "123", "123", "123", "123", "123", "123", "123", "123", "123", "123", "123", "123",
"12.3", "12.300000190734863", "12.3", "12.3",
"true", "false", "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++ { for i := 0; i < len(cases); i++ {
actual := ToString(cases[i]) actual := ToString(cases[i])
@@ -253,6 +249,7 @@ func TestDecodeByte(t *testing.T) {
var obj string var obj string
byteData := []byte{6, 12, 0, 3, 97, 98, 99} byteData := []byte{6, 12, 0, 3, 97, 98, 99}
DecodeByte(byteData, &obj) err := DecodeByte(byteData, &obj)
assert.IsNil(err)
assert.Equal("abc", obj) assert.Equal("abc", obj)
} }

View File

@@ -17,15 +17,19 @@ import (
// AesEcbEncrypt encrypt data with key use AES ECB algorithm // AesEcbEncrypt encrypt data with key use AES ECB algorithm
// len(key) should be 16, 24 or 32 // len(key) should be 16, 24 or 32
func AesEcbEncrypt(data, key []byte) []byte { func AesEcbEncrypt(data, key []byte) []byte {
cipher, _ := aes.NewCipher(generateAesKey(key))
length := (len(data) + aes.BlockSize) / aes.BlockSize length := (len(data) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize) plain := make([]byte, length*aes.BlockSize)
copy(plain, data) copy(plain, data)
pad := byte(len(plain) - len(data)) pad := byte(len(plain) - len(data))
for i := len(data); i < len(plain); i++ { for i := len(data); i < len(plain); i++ {
plain[i] = pad plain[i] = pad
} }
encrypted := make([]byte, len(plain)) encrypted := make([]byte, len(plain))
cipher, _ := aes.NewCipher(generateAesKey(key))
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be]) cipher.Encrypt(encrypted[bs:be], plain[bs:be])
} }
@@ -108,27 +112,32 @@ func AesCfbEncrypt(data, key []byte) []byte {
encrypted := make([]byte, aes.BlockSize+len(data)) encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize] iv := encrypted[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil { if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err) panic(err)
} }
stream := cipher.NewCFBEncrypter(block, iv) stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(encrypted[aes.BlockSize:], data) stream.XORKeyStream(encrypted[aes.BlockSize:], data)
return encrypted return encrypted
} }
// AesCfbDecrypt decrypt data with key use AES CFB algorithm // AesCfbDecrypt decrypt data with key use AES CFB algorithm
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32 // len(encrypted) should be great than 16, len(key) should be 16, 24 or 32
func AesCfbDecrypt(encrypted, key []byte) []byte { func AesCfbDecrypt(encrypted, key []byte) []byte {
block, _ := aes.NewCipher(key)
if len(encrypted) < aes.BlockSize { if len(encrypted) < aes.BlockSize {
panic("encrypted data is too short") panic("encrypted data is too short")
} }
block, _ := aes.NewCipher(key)
iv := encrypted[:aes.BlockSize] iv := encrypted[:aes.BlockSize]
encrypted = encrypted[aes.BlockSize:] encrypted = encrypted[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv) stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(encrypted, encrypted) stream.XORKeyStream(encrypted, encrypted)
return encrypted return encrypted
} }
@@ -139,6 +148,7 @@ func AesOfbEncrypt(data, key []byte) []byte {
if err != nil { if err != nil {
panic(err) panic(err)
} }
data = pkcs7Padding(data, aes.BlockSize) data = pkcs7Padding(data, aes.BlockSize)
encrypted := make([]byte, aes.BlockSize+len(data)) encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize] iv := encrypted[:aes.BlockSize]
@@ -148,6 +158,7 @@ func AesOfbEncrypt(data, key []byte) []byte {
stream := cipher.NewOFB(block, iv) stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(encrypted[aes.BlockSize:], data) stream.XORKeyStream(encrypted[aes.BlockSize:], data)
return encrypted return encrypted
} }
@@ -170,5 +181,6 @@ func AesOfbDecrypt(data, key []byte) []byte {
mode.XORKeyStream(decrypted, data) mode.XORKeyStream(decrypted, data)
decrypted = pkcs7UnPadding(decrypted) decrypted = pkcs7UnPadding(decrypted)
return decrypted return decrypted
} }

View File

@@ -15,7 +15,6 @@ import (
// DesEcbEncrypt encrypt data with key use DES ECB algorithm // DesEcbEncrypt encrypt data with key use DES ECB algorithm
// len(key) should be 8 // len(key) should be 8
func DesEcbEncrypt(data, key []byte) []byte { func DesEcbEncrypt(data, key []byte) []byte {
cipher, _ := des.NewCipher(generateDesKey(key))
length := (len(data) + des.BlockSize) / des.BlockSize length := (len(data) + des.BlockSize) / des.BlockSize
plain := make([]byte, length*des.BlockSize) plain := make([]byte, length*des.BlockSize)
copy(plain, data) copy(plain, data)
@@ -26,6 +25,8 @@ func DesEcbEncrypt(data, key []byte) []byte {
} }
encrypted := make([]byte, len(plain)) encrypted := make([]byte, len(plain))
cipher, _ := des.NewCipher(generateDesKey(key))
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be]) cipher.Encrypt(encrypted[bs:be], plain[bs:be])
} }
@@ -59,6 +60,7 @@ func DesCbcEncrypt(data, key []byte) []byte {
encrypted := make([]byte, des.BlockSize+len(data)) encrypted := make([]byte, des.BlockSize+len(data))
iv := encrypted[:des.BlockSize] iv := encrypted[:des.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil { if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err) panic(err)
} }

View File

@@ -33,7 +33,11 @@ func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
if err != nil { if err != nil {
panic(err) panic(err)
} }
pem.Encode(file, &block) err = pem.Encode(file, &block)
if err != nil {
return err
}
file.Close() file.Close()
// public key // public key
@@ -49,12 +53,16 @@ func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
Bytes: derpText, Bytes: derpText,
} }
//file,err = os.Create("rsa_public.pem")
file, err = os.Create(pubKeyFile) file, err = os.Create(pubKeyFile)
if err != nil { if err != nil {
return err return err
} }
pem.Encode(file, &block)
err = pem.Encode(file, &block)
if err != nil {
return err
}
file.Close() file.Close()
return nil return nil
@@ -72,7 +80,11 @@ func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
} }
defer file.Close() defer file.Close()
buf := make([]byte, fileInfo.Size()) buf := make([]byte, fileInfo.Size())
file.Read(buf)
_, err = file.Read(buf)
if err != nil {
panic(err)
}
block, _ := pem.Decode(buf) block, _ := pem.Decode(buf)
@@ -101,7 +113,11 @@ func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
} }
buf := make([]byte, fileInfo.Size()) buf := make([]byte, fileInfo.Size())
defer file.Close() defer file.Close()
file.Read(buf)
_, err = file.Read(buf)
if err != nil {
panic(err)
}
block, _ := pem.Decode(buf) block, _ := pem.Decode(buf)

View File

@@ -93,6 +93,46 @@ func (hm *HashMap) Contains(key any) bool {
return node != nil return node != nil
} }
// Iterate executes iteratee funcation for every key and value pair of hashmap (random order)
func (hm *HashMap) Iterate(iteratee func(key, value any)) {
if hm.size > 0 {
for i := 0; i < len(hm.table); i++ {
item := hm.table[i]
if item != nil {
iteratee(item.key, item.value)
}
}
}
}
// Keys returns a slice of the hashmap's keys (random order)
func (hm *HashMap) Keys() []any {
keys := make([]any, int(hm.size))
index := 0
if hm.size > 0 {
hm.Iterate(func(key, value any) {
keys[index] = key
index++
})
}
return keys
}
// Values returns a slice of the hashmap's keys (random order)
func (hm *HashMap) Values() []any {
values := make([]any, int(hm.size))
index := 0
if hm.size > 0 {
hm.Iterate(func(key, value any) {
values[index] = value
index++
})
}
return values
}
func (hm *HashMap) resize() { func (hm *HashMap) resize() {
hm.capacity <<= 1 hm.capacity <<= 1

View File

@@ -52,3 +52,20 @@ func TestHashMap_Contains(t *testing.T) {
hm.Put("abc", 3) hm.Put("abc", 3)
assert.Equal(true, hm.Contains("abc")) assert.Equal(true, hm.Contains("abc"))
} }
func TestHashMap_KeysValues(t *testing.T) {
assert := internal.NewAssert(t, "TestHashMap_KeysValues")
hm := NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
keys := hm.Keys()
values := hm.Values()
t.Log(keys, values)
assert.Equal(3, len(values))
assert.Equal(3, len(keys))
}

View File

@@ -146,9 +146,9 @@ func (h *MaxHeap[T]) PrintStructure() {
lastNum := powerTwo(level - 1) lastNum := powerTwo(level - 1)
lastLen := lastNum + (lastNum - 1) lastLen := lastNum + (lastNum - 1)
heapTree := make([][]string, level, level) heapTree := make([][]string, level)
for i := 0; i < level; i++ { for i := 0; i < level; i++ {
heapTree[i] = make([]string, lastLen, lastLen) heapTree[i] = make([]string, lastLen)
for j := 0; j < lastLen; j++ { for j := 0; j < lastLen; j++ {
heapTree[i][j] = "" heapTree[i][j] = ""
} }
@@ -169,9 +169,9 @@ func (h *MaxHeap[T]) PrintStructure() {
for n := 0; n < lastLen; n++ { for n := 0; n < lastLen; n++ {
val := heapTree[m][n] val := heapTree[m][n]
if val == "" { if val == "" {
fmt.Printf(" ") fmt.Print(" ")
} else { } else {
fmt.Printf(val) fmt.Print(val)
} }
} }
fmt.Println() fmt.Println()

View File

@@ -30,8 +30,6 @@ func TestMaxHeap_BuildMaxHeap(t *testing.T) {
assert.Equal(expected, heap.data) assert.Equal(expected, heap.data)
assert.Equal(12, heap.Size()) assert.Equal(12, heap.Size())
heap.PrintStructure()
} }
func TestMaxHeap_Push(t *testing.T) { func TestMaxHeap_Push(t *testing.T) {

View File

@@ -1,13 +1,12 @@
package datastructure package datastructure
import ( import (
"errors"
"fmt" "fmt"
"github.com/duke-git/lancet/v2/datastructure" "github.com/duke-git/lancet/v2/datastructure"
) )
// DoublyLink is a linked list. Whose node has a generic Value, Pre pointer points to a previous node of the link, Next pointer points to a next node of the link. // DoublyLink is a linked list. Whose node has a generic Value, Pre pointer points to a previous node of the dl, Next pointer points to a next node of the dl.
type DoublyLink[T any] struct { type DoublyLink[T any] struct {
Head *datastructure.LinkNode[T] Head *datastructure.LinkNode[T]
length int length int
@@ -19,30 +18,30 @@ func NewDoublyLink[T any]() *DoublyLink[T] {
} }
// InsertAtHead insert value into doubly linklist at head index // InsertAtHead insert value into doubly linklist at head index
func (link *DoublyLink[T]) InsertAtHead(value T) { func (dl *DoublyLink[T]) InsertAtHead(value T) {
newNode := datastructure.NewLinkNode(value) newNode := datastructure.NewLinkNode(value)
size := link.Size() size := dl.Size()
if size == 0 { if size == 0 {
link.Head = newNode dl.Head = newNode
link.length++ dl.length++
return return
} }
newNode.Next = link.Head newNode.Next = dl.Head
newNode.Pre = nil newNode.Pre = nil
link.Head.Pre = newNode dl.Head.Pre = newNode
link.Head = newNode dl.Head = newNode
link.length++ dl.length++
} }
// InsertAtTail insert value into doubly linklist at tail index // InsertAtTail insert value into doubly linklist at tail index
func (link *DoublyLink[T]) InsertAtTail(value T) { func (dl *DoublyLink[T]) InsertAtTail(value T) {
current := link.Head current := dl.Head
if current == nil { if current == nil {
link.InsertAtHead(value) dl.InsertAtHead(value)
return return
} }
@@ -55,28 +54,29 @@ func (link *DoublyLink[T]) InsertAtTail(value T) {
newNode.Pre = current newNode.Pre = current
current.Next = newNode current.Next = newNode
link.length++ dl.length++
} }
// InsertAt insert value into doubly linklist at index // InsertAt insert value into doubly linklist at index
func (link *DoublyLink[T]) InsertAt(index int, value T) error { // param `index` should between [0, length], if index do not meet the conditions, do nothing
size := link.length func (dl *DoublyLink[T]) InsertAt(index int, value T) {
size := dl.length
if index < 0 || index > size { if index < 0 || index > size {
return errors.New("param index should between 0 and the length of doubly link.") return
} }
if index == 0 { if index == 0 {
link.InsertAtHead(value) dl.InsertAtHead(value)
return nil return
} }
if index == size { if index == size {
link.InsertAtTail(value) dl.InsertAtTail(value)
return nil return
} }
i := 0 i := 0
current := link.Head current := dl.Head
for current != nil { for current != nil {
if i == index-1 { if i == index-1 {
@@ -85,38 +85,36 @@ func (link *DoublyLink[T]) InsertAt(index int, value T) error {
newNode.Pre = current newNode.Pre = current
current.Next = newNode current.Next = newNode
link.length++ dl.length++
return nil return
} }
i++ i++
current = current.Next current = current.Next
} }
return errors.New("doubly link list no exist")
} }
// DeleteAtHead delete value in doubly linklist at head index // DeleteAtHead delete value in doubly linklist at head index
func (link *DoublyLink[T]) DeleteAtHead() error { func (dl *DoublyLink[T]) DeleteAtHead() {
if link.Head == nil { if dl.Head == nil {
return errors.New("doubly link list no exist") return
} }
current := link.Head
link.Head = current.Next
link.Head.Pre = nil
link.length--
return nil current := dl.Head
dl.Head = current.Next
dl.Head.Pre = nil
dl.length--
} }
// DeleteAtTail delete value in doubly linklist at tail index // DeleteAtTail delete value in doubly linklist at tail
func (link *DoublyLink[T]) DeleteAtTail() error { func (dl *DoublyLink[T]) DeleteAtTail() {
if link.Head == nil { if dl.Head == nil {
return errors.New("doubly link list no exist") return
} }
current := link.Head
current := dl.Head
if current.Next == nil { if current.Next == nil {
return link.DeleteAtHead() dl.DeleteAtHead()
} }
for current.Next.Next != nil { for current.Next.Next != nil {
@@ -124,45 +122,44 @@ func (link *DoublyLink[T]) DeleteAtTail() error {
} }
current.Next = nil current.Next = nil
link.length-- dl.length--
return nil
} }
// DeleteAt delete value in doubly linklist at index // DeleteAt delete value in doubly linklist at index
func (link *DoublyLink[T]) DeleteAt(index int) error { // param `index` should be [0, len(DoublyLink)-1]
if link.Head == nil { func (dl *DoublyLink[T]) DeleteAt(index int) {
return errors.New("doubly link list no exist") if dl.Head == nil {
return
} }
current := link.Head
current := dl.Head
if current.Next == nil || index == 0 { if current.Next == nil || index == 0 {
return link.DeleteAtHead() dl.DeleteAtHead()
} }
if index == link.length-1 { if index == dl.length-1 {
return link.DeleteAtTail() dl.DeleteAtTail()
} }
if index < 0 || index > link.length-1 { if index < 0 || index > dl.length-1 {
return errors.New("param index should between 0 and link size -1.") return
} }
i := 0 i := 0
for current != nil { for current != nil {
if i == index-1 { if i == index-1 {
current.Next = current.Next.Next current.Next = current.Next.Next
link.length-- dl.length--
return nil return
} }
i++ i++
current = current.Next current = current.Next
} }
return errors.New("delete error")
} }
// Reverse the linked list // Reverse the linked list
func (link *DoublyLink[T]) Reverse() { func (dl *DoublyLink[T]) Reverse() {
current := link.Head current := dl.Head
var temp *datastructure.LinkNode[T] var temp *datastructure.LinkNode[T]
for current != nil { for current != nil {
@@ -173,20 +170,20 @@ func (link *DoublyLink[T]) Reverse() {
} }
if temp != nil { if temp != nil {
link.Head = temp.Pre dl.Head = temp.Pre
} }
} }
// GetMiddleNode return node at middle index of linked list // GetMiddleNode return node at middle index of linked list
func (link *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] { func (dl *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
if link.Head == nil { if dl.Head == nil {
return nil return nil
} }
if link.Head.Next == nil { if dl.Head.Next == nil {
return link.Head return dl.Head
} }
fast := link.Head fast := dl.Head
slow := link.Head slow := dl.Head
for fast != nil { for fast != nil {
fast = fast.Next fast = fast.Next
@@ -202,14 +199,14 @@ func (link *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
} }
// Size return the count of doubly linked list // Size return the count of doubly linked list
func (link *DoublyLink[T]) Size() int { func (dl *DoublyLink[T]) Size() int {
return link.length return dl.length
} }
// Values return slice of all doubly linklist node value // Values return slice of all doubly linklist node value
func (link *DoublyLink[T]) Values() []T { func (dl *DoublyLink[T]) Values() []T {
result := []T{} result := []T{}
current := link.Head current := dl.Head
for current != nil { for current != nil {
result = append(result, current.Value) result = append(result, current.Value)
current = current.Next current = current.Next
@@ -218,8 +215,8 @@ func (link *DoublyLink[T]) Values() []T {
} }
// Print all nodes info of a linked list // Print all nodes info of a linked list
func (link *DoublyLink[T]) Print() { func (dl *DoublyLink[T]) Print() {
current := link.Head current := dl.Head
info := "[ " info := "[ "
for current != nil { for current != nil {
info += fmt.Sprintf("%+v, ", current) info += fmt.Sprintf("%+v, ", current)
@@ -229,13 +226,13 @@ func (link *DoublyLink[T]) Print() {
fmt.Println(info) fmt.Println(info)
} }
// IsEmpty checks if link is empty or not // IsEmpty checks if dl is empty or not
func (link *DoublyLink[T]) IsEmpty() bool { func (dl *DoublyLink[T]) IsEmpty() bool {
return link.length == 0 return dl.length == 0
} }
// Clear all nodes in doubly linklist // Clear all nodes in doubly linklist
func (link *DoublyLink[T]) Clear() { func (dl *DoublyLink[T]) Clear() {
link.Head = nil dl.Head = nil
link.length = 0 dl.length = 0
} }

View File

@@ -41,29 +41,24 @@ func TestDoublyLink_InsertAt(t *testing.T) {
link := NewDoublyLink[int]() link := NewDoublyLink[int]()
err := link.InsertAt(1, 1) link.InsertAt(1, 1) //do nothing
assert.IsNotNil(err)
link.InsertAt(0, 1) link.InsertAt(0, 1)
link.InsertAt(1, 2) link.InsertAt(1, 2)
link.InsertAt(2, 4) link.InsertAt(2, 4)
link.InsertAt(2, 3) link.InsertAt(2, 3)
link.Print()
expected := []int{1, 2, 3, 4} expected := []int{1, 2, 3, 4}
values := link.Values() values := link.Values()
assert.Equal(expected, values) assert.Equal(expected, values)
} }
func TestDoublyLink_DeleteAtHead(t *testing.T) { func TestDoublyLink_DeleteAtHead(t *testing.T) {
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtHead") assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtHead")
link := NewDoublyLink[int]() link := NewDoublyLink[int]()
err := link.DeleteAtHead() link.DeleteAtHead()
assert.IsNotNil(err)
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -71,7 +66,6 @@ func TestDoublyLink_DeleteAtHead(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.DeleteAtHead() link.DeleteAtHead()
link.Print()
expected := []int{2, 3, 4} expected := []int{2, 3, 4}
values := link.Values() values := link.Values()
@@ -83,8 +77,7 @@ func TestDoublyLink_DeleteAtTail(t *testing.T) {
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtTail") assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtTail")
link := NewDoublyLink[int]() link := NewDoublyLink[int]()
err := link.DeleteAtTail() link.DeleteAtTail()
assert.IsNotNil(err)
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -92,7 +85,6 @@ func TestDoublyLink_DeleteAtTail(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.DeleteAtTail() link.DeleteAtTail()
link.Print()
expected := []int{1, 2, 3} expected := []int{1, 2, 3}
values := link.Values() values := link.Values()
@@ -104,8 +96,7 @@ func TestDoublyLink_DeleteAt(t *testing.T) {
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAt") assert := internal.NewAssert(t, "TestDoublyLink_DeleteAt")
link := NewDoublyLink[int]() link := NewDoublyLink[int]()
err := link.DeleteAt(0) link.DeleteAt(0)
assert.IsNotNil(err)
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -113,11 +104,7 @@ func TestDoublyLink_DeleteAt(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.InsertAtTail(5) link.InsertAtTail(5)
err = link.DeleteAt(5) link.DeleteAt(0)
assert.IsNotNil(err)
err = link.DeleteAt(0)
assert.IsNil(err)
assert.Equal([]int{2, 3, 4, 5}, link.Values()) assert.Equal([]int{2, 3, 4, 5}, link.Values())
link.DeleteAt(3) link.DeleteAt(3)

View File

@@ -1,14 +1,13 @@
package datastructure package datastructure
import ( import (
"errors"
"fmt" "fmt"
"reflect" "reflect"
"github.com/duke-git/lancet/v2/datastructure" "github.com/duke-git/lancet/v2/datastructure"
) )
// SinglyLink is a linked list. Whose node has a Value generics and Next pointer points to a next node of the link. // SinglyLink is a linked list. Whose node has a Value generics and Next pointer points to a next node of the sl.
type SinglyLink[T any] struct { type SinglyLink[T any] struct {
Head *datastructure.LinkNode[T] Head *datastructure.LinkNode[T]
length int length int
@@ -20,18 +19,18 @@ func NewSinglyLink[T any]() *SinglyLink[T] {
} }
// InsertAtHead insert value into singly linklist at head index // InsertAtHead insert value into singly linklist at head index
func (link *SinglyLink[T]) InsertAtHead(value T) { func (sl *SinglyLink[T]) InsertAtHead(value T) {
newNode := datastructure.NewLinkNode(value) newNode := datastructure.NewLinkNode(value)
newNode.Next = link.Head newNode.Next = sl.Head
link.Head = newNode sl.Head = newNode
link.length++ sl.length++
} }
// InsertAtTail insert value into singly linklist at tail index // InsertAtTail insert value into singly linklist at tail index
func (link *SinglyLink[T]) InsertAtTail(value T) { func (sl *SinglyLink[T]) InsertAtTail(value T) {
current := link.Head current := sl.Head
if current == nil { if current == nil {
link.InsertAtHead(value) sl.InsertAtHead(value)
return return
} }
@@ -43,65 +42,63 @@ func (link *SinglyLink[T]) InsertAtTail(value T) {
newNode.Next = nil newNode.Next = nil
current.Next = newNode current.Next = newNode
link.length++ sl.length++
} }
// InsertAt insert value into singly linklist at index // InsertAt insert value into singly linklist at index
func (link *SinglyLink[T]) InsertAt(index int, value T) error { // param `index` should between [0, len(SinglyLink)], if index do not meet the conditions, do nothing
size := link.length func (sl *SinglyLink[T]) InsertAt(index int, value T) {
size := sl.length
if index < 0 || index > size { if index < 0 || index > size {
return errors.New("param index should between 0 and the length of singly link.") return
} }
if index == 0 { if index == 0 {
link.InsertAtHead(value) sl.InsertAtHead(value)
return nil return
} }
if index == size { if index == size {
link.InsertAtTail(value) sl.InsertAtTail(value)
return nil return
} }
i := 0 i := 0
current := link.Head current := sl.Head
for current != nil { for current != nil {
if i == index-1 { if i == index-1 {
newNode := datastructure.NewLinkNode(value) newNode := datastructure.NewLinkNode(value)
newNode.Next = current.Next newNode.Next = current.Next
current.Next = newNode current.Next = newNode
link.length++ sl.length++
return
return nil
} }
i++ i++
current = current.Next current = current.Next
} }
return errors.New("singly link list no exist")
} }
// DeleteAtHead delete value in singly linklist at head index // DeleteAtHead delete value in singly linklist at head index
func (link *SinglyLink[T]) DeleteAtHead() error { func (sl *SinglyLink[T]) DeleteAtHead() {
if link.Head == nil { if sl.Head == nil {
return errors.New("singly link list no exist") return
} }
current := link.Head
link.Head = current.Next
link.length--
return nil current := sl.Head
sl.Head = current.Next
sl.length--
} }
// DeleteAtTail delete value in singly linklist at tail index // DeleteAtTail delete value in singly linklist at tail
func (link *SinglyLink[T]) DeleteAtTail() error { func (sl *SinglyLink[T]) DeleteAtTail() {
if link.Head == nil { if sl.Head == nil {
return errors.New("singly link list no exist") return
} }
current := link.Head
current := sl.Head
if current.Next == nil { if current.Next == nil {
return link.DeleteAtHead() sl.DeleteAtHead()
} }
for current.Next.Next != nil { for current.Next.Next != nil {
@@ -109,68 +106,66 @@ func (link *SinglyLink[T]) DeleteAtTail() error {
} }
current.Next = nil current.Next = nil
link.length-- sl.length--
return nil
} }
// DeleteAt delete value in singly linklist at index // DeleteAt delete value in singly linklist at index
func (link *SinglyLink[T]) DeleteAt(index int) error { // param `index` should be [0, len(SinglyLink)-1]
if link.Head == nil { func (sl *SinglyLink[T]) DeleteAt(index int) {
return errors.New("singly link list no exist") if sl.Head == nil {
return
} }
current := link.Head current := sl.Head
if current.Next == nil || index == 0 { if current.Next == nil || index == 0 {
return link.DeleteAtHead() sl.DeleteAtHead()
} }
if index == link.length-1 { if index == sl.length-1 {
return link.DeleteAtTail() sl.DeleteAtTail()
} }
if index < 0 || index > link.length-1 { if index < 0 || index > sl.length-1 {
return errors.New("param index should between 0 and link size -1.") return
} }
i := 0 i := 0
for current != nil { for current != nil {
if i == index-1 { if i == index-1 {
current.Next = current.Next.Next current.Next = current.Next.Next
link.length-- sl.length--
return nil return
} }
i++ i++
current = current.Next current = current.Next
} }
return errors.New("delete error")
} }
// DeleteValue delete value in singly linklist // DeleteValue delete value in singly linklist
func (link *SinglyLink[T]) DeleteValue(value T) { func (sl *SinglyLink[T]) DeleteValue(value T) {
if link.Head == nil { if sl.Head == nil {
return return
} }
dummyHead := datastructure.NewLinkNode(value) dummyHead := datastructure.NewLinkNode(value)
dummyHead.Next = link.Head dummyHead.Next = sl.Head
current := dummyHead current := dummyHead
for current.Next != nil { for current.Next != nil {
if reflect.DeepEqual(current.Next.Value, value) { if reflect.DeepEqual(current.Next.Value, value) {
current.Next = current.Next.Next current.Next = current.Next.Next
link.length-- sl.length--
} else { } else {
current = current.Next current = current.Next
} }
} }
link.Head = dummyHead.Next sl.Head = dummyHead.Next
} }
// Reverse the linked list // Reverse the linked list
func (link *SinglyLink[T]) Reverse() { func (sl *SinglyLink[T]) Reverse() {
var pre, next *datastructure.LinkNode[T] var pre, next *datastructure.LinkNode[T]
current := link.Head current := sl.Head
for current != nil { for current != nil {
next = current.Next next = current.Next
@@ -179,19 +174,19 @@ func (link *SinglyLink[T]) Reverse() {
current = next current = next
} }
link.Head = pre sl.Head = pre
} }
// GetMiddleNode return node at middle index of linked list // GetMiddleNode return node at middle index of linked list
func (link *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] { func (sl *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
if link.Head == nil { if sl.Head == nil {
return nil return nil
} }
if link.Head.Next == nil { if sl.Head.Next == nil {
return link.Head return sl.Head
} }
fast := link.Head fast := sl.Head
slow := link.Head slow := sl.Head
for fast != nil { for fast != nil {
fast = fast.Next fast = fast.Next
@@ -207,14 +202,14 @@ func (link *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
} }
// Size return the count of singly linked list // Size return the count of singly linked list
func (link *SinglyLink[T]) Size() int { func (sl *SinglyLink[T]) Size() int {
return link.length return sl.length
} }
// Values return slice of all singly linklist node value // Values return slice of all singly linklist node value
func (link *SinglyLink[T]) Values() []T { func (sl *SinglyLink[T]) Values() []T {
result := []T{} result := []T{}
current := link.Head current := sl.Head
for current != nil { for current != nil {
result = append(result, current.Value) result = append(result, current.Value)
current = current.Next current = current.Next
@@ -222,20 +217,20 @@ func (link *SinglyLink[T]) Values() []T {
return result return result
} }
// IsEmpty checks if link is empty or not // IsEmpty checks if sl is empty or not
func (link *SinglyLink[T]) IsEmpty() bool { func (sl *SinglyLink[T]) IsEmpty() bool {
return link.length == 0 return sl.length == 0
} }
// Clear all the node in singly linklist // Clear all the node in singly linklist
func (link *SinglyLink[T]) Clear() { func (sl *SinglyLink[T]) Clear() {
link.Head = nil sl.Head = nil
link.length = 0 sl.length = 0
} }
// Print all nodes info of a linked list // Print all nodes info of a linked list
func (link *SinglyLink[T]) Print() { func (sl *SinglyLink[T]) Print() {
current := link.Head current := sl.Head
info := "[ " info := "[ "
for current != nil { for current != nil {
info += fmt.Sprintf("%+v, ", current) info += fmt.Sprintf("%+v, ", current)

View File

@@ -41,25 +41,12 @@ func TestSinglyLink_InsertAt(t *testing.T) {
link := NewSinglyLink[int]() link := NewSinglyLink[int]()
err := link.InsertAt(1, 1) link.InsertAt(1, 1) //do nothing
assert.IsNotNil(err)
err = link.InsertAt(0, 1) link.InsertAt(0, 1)
if err != nil { link.InsertAt(1, 2)
t.FailNow() link.InsertAt(2, 4)
} link.InsertAt(2, 3)
err = link.InsertAt(1, 2)
if err != nil {
t.FailNow()
}
err = link.InsertAt(2, 4)
if err != nil {
t.FailNow()
}
err = link.InsertAt(2, 3)
if err != nil {
t.FailNow()
}
link.Print() link.Print()
@@ -73,8 +60,8 @@ func TestSinglyLink_DeleteAtHead(t *testing.T) {
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtHead") assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtHead")
link := NewSinglyLink[int]() link := NewSinglyLink[int]()
err := link.DeleteAtHead()
assert.IsNotNil(err) link.DeleteAtHead()
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -94,8 +81,6 @@ func TestSinglyLink_DeleteAtTail(t *testing.T) {
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtTail") assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtTail")
link := NewSinglyLink[int]() link := NewSinglyLink[int]()
err := link.DeleteAtTail()
assert.IsNotNil(err)
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -103,7 +88,6 @@ func TestSinglyLink_DeleteAtTail(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.DeleteAtTail() link.DeleteAtTail()
link.Print()
expected := []int{1, 2, 3} expected := []int{1, 2, 3}
values := link.Values() values := link.Values()
@@ -133,8 +117,6 @@ func TestSinglyLink_DeleteAt(t *testing.T) {
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAt") assert := internal.NewAssert(t, "TestSinglyLink_DeleteAt")
link := NewSinglyLink[int]() link := NewSinglyLink[int]()
err := link.DeleteAt(0)
assert.IsNotNil(err)
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -142,11 +124,7 @@ func TestSinglyLink_DeleteAt(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.InsertAtTail(5) link.InsertAtTail(5)
err = link.DeleteAt(5) link.DeleteAt(0)
assert.IsNotNil(err)
err = link.DeleteAt(0)
assert.IsNil(err)
assert.Equal([]int{2, 3, 4, 5}, link.Values()) assert.Equal([]int{2, 3, 4, 5}, link.Values())
link.DeleteAt(3) link.DeleteAt(3)
@@ -167,7 +145,6 @@ func TestSinglyLink_Reverse(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.Reverse() link.Reverse()
link.Print()
assert.Equal([]int{4, 3, 2, 1}, link.Values()) assert.Equal([]int{4, 3, 2, 1}, link.Values())
} }

View File

@@ -156,7 +156,7 @@ func (l *List[T]) DeleteAt(index int) {
return return
} }
if index == size-1 { if index == size-1 {
data = append(data[:index]) data = data[:index]
} else { } else {
data = append(data[:index], data[index+1:]...) data = append(data[:index], data[index+1:]...)
} }
@@ -174,7 +174,7 @@ func (l *List[T]) DeleteIf(f func(T) bool) int {
continue continue
} }
if index == size-1 { if index == size-1 {
data = append(data[:index]) data = data[:index]
} else { } else {
data = append(data[:index], data[index+1:]...) data = append(data[:index], data[index+1:]...)
index-- index--
@@ -221,7 +221,7 @@ func (l *List[T]) IsEmpty() bool {
// Clear the data of list // Clear the data of list
func (l *List[T]) Clear() { func (l *List[T]) Clear() {
l.data = make([]T, 0, 0) l.data = make([]T, 0)
} }
// Clone return a copy of list // Clone return a copy of list
@@ -235,7 +235,7 @@ func (l *List[T]) Clone() *List[T] {
// Merge two list, return new list, don't change original list // Merge two list, return new list, don't change original list
func (l *List[T]) Merge(other *List[T]) *List[T] { func (l *List[T]) Merge(other *List[T]) *List[T] {
l1, l2 := len(l.data), len(other.data) l1, l2 := len(l.data), len(other.data)
ml := NewList(make([]T, l1+l2, l1+l2)) ml := NewList(make([]T, l1+l2))
data := append([]T{}, append(l.data, other.data...)...) data := append([]T{}, append(l.data, other.data...)...)
ml.data = data ml.data = data
@@ -274,7 +274,7 @@ func (l *List[T]) Unique() {
data := l.data data := l.data
size := len(data) size := len(data)
uniqueData := make([]T, 0, 0) uniqueData := make([]T, 0)
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
value := data[i] value := data[i]
skip := true skip := true
@@ -305,7 +305,7 @@ func (l *List[T]) Union(other *List[T]) *List[T] {
// Intersection creates a new list whose element both be contained in list l and other // Intersection creates a new list whose element both be contained in list l and other
func (l *List[T]) Intersection(other *List[T]) *List[T] { func (l *List[T]) Intersection(other *List[T]) *List[T] {
result := NewList(make([]T, 0, 0)) result := NewList(make([]T, 0))
for _, v := range l.data { for _, v := range l.data {
if other.Contain(v) { if other.Contain(v) {

View File

@@ -18,8 +18,6 @@ func TestArrayQueue_Enqueue(t *testing.T) {
data := queue.Data() data := queue.Data()
size := queue.Size() size := queue.Size()
queue.Print()
assert.Equal(expected, data) assert.Equal(expected, data)
assert.Equal(3, size) assert.Equal(3, size)
} }
@@ -35,7 +33,6 @@ func TestArrayQueue_Dequeue(t *testing.T) {
val, ok := queue.Dequeue() val, ok := queue.Dequeue()
assert.Equal(true, ok) assert.Equal(true, ok)
queue.Print()
assert.Equal(1, val) assert.Equal(1, val)
assert.Equal([]int{2, 3}, queue.Data()) assert.Equal([]int{2, 3}, queue.Data())
} }
@@ -50,8 +47,6 @@ func TestArrayQueue_Front(t *testing.T) {
val := queue.Front() val := queue.Front()
queue.Print()
assert.Equal(1, val) assert.Equal(1, val)
assert.Equal([]int{1, 2, 3}, queue.Data()) assert.Equal([]int{1, 2, 3}, queue.Data())
} }
@@ -66,8 +61,6 @@ func TestArrayQueue_Back(t *testing.T) {
val := queue.Back() val := queue.Back()
queue.Print()
assert.Equal(3, val) assert.Equal(3, val)
assert.Equal([]int{1, 2, 3}, queue.Data()) assert.Equal([]int{1, 2, 3}, queue.Data())
} }

View File

@@ -10,31 +10,43 @@ func TestCircularQueue_Enqueue(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_Enqueue") assert := internal.NewAssert(t, "TestCircularQueue_Enqueue")
queue := NewCircularQueue[int](6) queue := NewCircularQueue[int](6)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
queue.Enqueue(4)
queue.Enqueue(5)
queue.Print() err := queue.Enqueue(1)
assert.IsNil(err)
err = queue.Enqueue(2)
assert.IsNil(err)
err = queue.Enqueue(3)
assert.IsNil(err)
err = queue.Enqueue(4)
assert.IsNil(err)
err = queue.Enqueue(5)
assert.IsNil(err)
assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data()) assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data())
assert.Equal(5, queue.Size()) assert.Equal(5, queue.Size())
err := queue.Enqueue(6) err = queue.Enqueue(6)
assert.IsNotNil(err) assert.IsNotNil(err)
} }
func TestCircularQueue_Dequeue(t *testing.T) { func TestCircularQueue_Dequeue(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_DeQueue") assert := internal.NewAssert(t, "TestCircularQueue_DeQueue")
queue := NewCircularQueue[int](6) queue := NewCircularQueue[int](4)
assert.Equal(true, queue.IsEmpty()) assert.Equal(true, queue.IsEmpty())
queue.Enqueue(1) err := queue.Enqueue(1)
queue.Enqueue(2) assert.IsNil(err)
queue.Enqueue(3)
queue.Enqueue(4) err = queue.Enqueue(2)
queue.Enqueue(5) assert.IsNil(err)
err = queue.Enqueue(3)
assert.IsNil(err)
val, err := queue.Dequeue() val, err := queue.Dequeue()
assert.IsNil(err) assert.IsNil(err)
@@ -43,11 +55,7 @@ func TestCircularQueue_Dequeue(t *testing.T) {
assert.Equal(false, queue.IsFull()) assert.Equal(false, queue.IsFull())
val, _ = queue.Dequeue() val, _ = queue.Dequeue()
queue.Print()
assert.Equal(2, *val) assert.Equal(2, *val)
queue.Enqueue(6)
queue.Print()
assert.Equal(false, queue.IsFull()) assert.Equal(false, queue.IsFull())
} }
@@ -55,55 +63,52 @@ func TestCircularQueue_Front(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_Front") assert := internal.NewAssert(t, "TestCircularQueue_Front")
queue := NewCircularQueue[int](6) queue := NewCircularQueue[int](6)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
queue.Enqueue(4)
queue.Enqueue(5)
queue.Print() err := queue.Enqueue(1)
assert.IsNil(err)
queue.Dequeue() err = queue.Enqueue(2)
queue.Dequeue() assert.IsNil(err)
queue.Enqueue(6)
queue.Enqueue(7)
queue.Print() err = queue.Enqueue(3)
assert.IsNil(err)
val := queue.Front() val := queue.Front()
assert.Equal(3, val) assert.IsNil(err)
assert.Equal(5, queue.Size()) assert.Equal(1, val)
assert.Equal(3, queue.Size())
} }
func TestCircularQueue_Back(t *testing.T) { func TestCircularQueue_Back(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_Back") assert := internal.NewAssert(t, "TestCircularQueue_Back")
queue := NewCircularQueue[int](6) queue := NewCircularQueue[int](3)
assert.Equal(true, queue.IsEmpty()) assert.Equal(true, queue.IsEmpty())
queue.Enqueue(1) err := queue.Enqueue(1)
queue.Enqueue(2) assert.IsNil(err)
queue.Enqueue(3)
queue.Enqueue(4)
queue.Enqueue(5)
queue.Print() err = queue.Enqueue(2)
assert.Equal(5, queue.Back()) assert.IsNil(err)
queue.Dequeue() assert.Equal(2, queue.Back())
queue.Dequeue()
queue.Enqueue(6)
queue.Enqueue(7)
queue.Print() val, _ := queue.Dequeue()
assert.Equal(7, queue.Back()) assert.Equal(1, *val)
err = queue.Enqueue(3)
assert.IsNil(err)
assert.Equal(3, queue.Back())
} }
func TestCircularQueue_Contain(t *testing.T) { func TestCircularQueue_Contain(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_Contain") assert := internal.NewAssert(t, "TestCircularQueue_Contain")
queue := NewCircularQueue[int](2) queue := NewCircularQueue[int](2)
queue.Enqueue(1) err := queue.Enqueue(1)
assert.IsNil(err)
assert.Equal(true, queue.Contain(1)) assert.Equal(true, queue.Contain(1))
assert.Equal(false, queue.Contain(2)) assert.Equal(false, queue.Contain(2))
} }
@@ -115,7 +120,9 @@ func TestCircularQueue_Clear(t *testing.T) {
assert.Equal(true, queue.IsEmpty()) assert.Equal(true, queue.IsEmpty())
assert.Equal(0, queue.Size()) assert.Equal(0, queue.Size())
queue.Enqueue(1) err := queue.Enqueue(1)
assert.IsNil(err)
assert.Equal(false, queue.IsEmpty()) assert.Equal(false, queue.IsEmpty())
assert.Equal(1, queue.Size()) assert.Equal(1, queue.Size())
@@ -127,22 +134,12 @@ func TestCircularQueue_Clear(t *testing.T) {
func TestCircularQueue_Data(t *testing.T) { func TestCircularQueue_Data(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_Data") assert := internal.NewAssert(t, "TestCircularQueue_Data")
queue := NewCircularQueue[int](6) queue := NewCircularQueue[int](3)
queue.Enqueue(1) err := queue.Enqueue(1)
queue.Enqueue(2) assert.IsNil(err)
queue.Enqueue(3)
queue.Enqueue(4)
queue.Enqueue(5)
queue.Print() err = queue.Enqueue(2)
assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data()) assert.IsNil(err)
queue.Dequeue()
queue.Dequeue()
queue.Enqueue(6)
queue.Enqueue(7)
queue.Print()
assert.Equal([]int{3, 4, 5, 6, 7}, queue.Data())
assert.Equal([]int{1, 2}, queue.Data())
} }

View File

@@ -14,8 +14,6 @@ func TestLinkedQueue_Enqueue(t *testing.T) {
queue.Enqueue(2) queue.Enqueue(2)
queue.Enqueue(3) queue.Enqueue(3)
queue.Print()
assert.Equal([]int{1, 2, 3}, queue.Data()) assert.Equal([]int{1, 2, 3}, queue.Data())
assert.Equal(3, queue.Size()) assert.Equal(3, queue.Size())
} }

View File

@@ -23,19 +23,24 @@ func TestPriorityQueue_Enqueue(t *testing.T) {
assert := internal.NewAssert(t, "TestPriorityQueue_Enqueue") assert := internal.NewAssert(t, "TestPriorityQueue_Enqueue")
comparator := &intComparator{} comparator := &intComparator{}
pq := NewPriorityQueue[int](10, comparator) pq := NewPriorityQueue[int](3, comparator)
assert.Equal(true, pq.IsEmpty()) assert.Equal(true, pq.IsEmpty())
assert.Equal(false, pq.IsFull()) assert.Equal(false, pq.IsFull())
for i := 1; i < 11; i++ { err := pq.Enqueue(1)
pq.Enqueue(i) assert.IsNil(err)
}
err = pq.Enqueue(2)
assert.IsNil(err)
err = pq.Enqueue(3)
assert.IsNil(err)
assert.Equal(true, pq.IsFull()) assert.Equal(true, pq.IsFull())
queueData := pq.Data() queueData := pq.Data()
assert.Equal([]int{10, 9, 6, 7, 8, 2, 5, 1, 4, 3}, queueData) assert.Equal([]int{3, 1, 2}, queueData)
} }
@@ -43,22 +48,23 @@ func TestPriorityQueue_Dequeue(t *testing.T) {
assert := internal.NewAssert(t, "TestPriorityQueue_Dequeue") assert := internal.NewAssert(t, "TestPriorityQueue_Dequeue")
comparator := &intComparator{} comparator := &intComparator{}
pq := NewPriorityQueue[int](10, comparator) pq := NewPriorityQueue[int](3, comparator)
_, ok := pq.Dequeue() _, ok := pq.Dequeue()
assert.Equal(false, ok) assert.Equal(false, ok)
for i := 1; i < 11; i++ { err := pq.Enqueue(1)
pq.Enqueue(i) assert.IsNil(err)
}
assert.Equal(10, pq.Size()) err = pq.Enqueue(2)
assert.IsNil(err)
err = pq.Enqueue(3)
assert.IsNil(err)
assert.Equal(3, pq.Size())
val, ok := pq.Dequeue() val, ok := pq.Dequeue()
assert.Equal(true, ok) assert.Equal(true, ok)
assert.Equal(10, val) assert.Equal(3, val)
assert.Equal([]int{9, 8, 6, 7, 3, 2, 5, 1, 4}, pq.Data())
assert.Equal(9, pq.Size())
} }

View File

@@ -4,22 +4,59 @@ 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 // NewSetFromSlice create a set from slice
func (s Set[T]) Add(values ...T) { func NewSetFromSlice[T comparable](items []T) Set[T] {
for _, v := range values { 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{}{} s[v] = struct{}{}
} }
} }
// Contain checks if set contains value or not // AddIfNotExist checks if item exists in the set,
func (s Set[T]) Contain(value T) bool { // it adds the item to set and returns true if it does not exist in the set,
_, ok := s[value] // 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 return ok
} }
@@ -41,9 +78,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)
} }
} }
@@ -58,7 +95,7 @@ func (s Set[T]) Equal(other Set[T]) bool {
} }
// Iterate call function by every element of set // 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 { for v := range s {
fn(v) fn(v)
} }
@@ -76,13 +113,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

View File

@@ -6,6 +6,19 @@ import (
"github.com/duke-git/lancet/v2/internal" "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) { func TestSet_Add(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Add") assert := internal.NewAssert(t, "TestSet_Add")
@@ -17,6 +30,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")

View File

@@ -14,8 +14,6 @@ func TestLinkedStack_Push(t *testing.T) {
stack.Push(2) stack.Push(2)
stack.Push(3) stack.Push(3)
stack.Print()
expected := []int{3, 2, 1} expected := []int{3, 2, 1}
values := stack.Data() values := stack.Data()
size := stack.Size() size := stack.Size()

View File

@@ -27,8 +27,6 @@ func TestBSTree_Insert(t *testing.T) {
bstree.Insert(5) bstree.Insert(5)
bstree.Insert(2) bstree.Insert(2)
bstree.Insert(4) bstree.Insert(4)
bstree.Print()
} }
func TestBSTree_PreOrderTraverse(t *testing.T) { func TestBSTree_PreOrderTraverse(t *testing.T) {
@@ -86,8 +84,6 @@ func TestBSTree_LevelOrderTraverse(t *testing.T) {
bstree.Insert(2) bstree.Insert(2)
bstree.Insert(4) bstree.Insert(4)
bstree.Print()
acturl := bstree.LevelOrderTraverse() acturl := bstree.LevelOrderTraverse()
t.Log(acturl) t.Log(acturl)
assert.Equal([]int{6, 5, 7, 2, 4}, acturl) assert.Equal([]int{6, 5, 7, 2, 4}, acturl)
@@ -103,10 +99,8 @@ func TestBSTree_Delete(t *testing.T) {
bstree.Insert(2) bstree.Insert(2)
bstree.Insert(4) bstree.Insert(4)
bstree.Print()
bstree.Delete(4) bstree.Delete(4)
bstree.Print()
acturl1 := bstree.InOrderTraverse() acturl1 := bstree.InOrderTraverse()
t.Log(acturl1) t.Log(acturl1)
assert.Equal([]int{2, 5, 6, 7}, acturl1) assert.Equal([]int{2, 5, 6, 7}, acturl1)
@@ -129,8 +123,6 @@ func TestBSTree_Depth(t *testing.T) {
bstree.Insert(2) bstree.Insert(2)
bstree.Insert(4) bstree.Insert(4)
bstree.Print()
assert.Equal(bstree.Depth(), 4) assert.Equal(bstree.Depth(), 4)
} }
@@ -150,8 +142,6 @@ func TestBSTree_IsSubTree(t *testing.T) {
subTree.Insert(4) subTree.Insert(4)
subTree.Insert(6) subTree.Insert(6)
subTree.Print()
assert.Equal(true, superTree.HasSubTree(subTree)) assert.Equal(true, superTree.HasSubTree(subTree))
assert.Equal(false, subTree.HasSubTree(superTree)) assert.Equal(false, subTree.HasSubTree(superTree))
} }

View File

@@ -38,35 +38,35 @@ func inOrderTraverse[T any](node *datastructure.TreeNode[T]) []T {
return data return data
} }
func preOrderPrint[T any](node *datastructure.TreeNode[T]) { // func preOrderPrint[T any](node *datastructure.TreeNode[T]) {
if node == nil { // if node == nil {
return // return
} // }
fmt.Printf("%v, ", node.Value) // fmt.Printf("%v, ", node.Value)
preOrderPrint(node.Left) // preOrderPrint(node.Left)
preOrderPrint(node.Right) // preOrderPrint(node.Right)
} // }
func postOrderPrint[T any](node *datastructure.TreeNode[T]) { // func postOrderPrint[T any](node *datastructure.TreeNode[T]) {
if node == nil { // if node == nil {
return // return
} // }
preOrderPrint(node.Left) // postOrderPrint(node.Left)
preOrderPrint(node.Right) // postOrderPrint(node.Right)
fmt.Printf("%v, ", node.Value) // fmt.Printf("%v, ", node.Value)
} // }
func inOrderPrint[T any](node *datastructure.TreeNode[T]) { // func inOrderPrint[T any](node *datastructure.TreeNode[T]) {
if node == nil { // if node == nil {
return // return
} // }
inOrderPrint(node.Left) // inOrderPrint(node.Left)
fmt.Printf("%v, ", node.Value) // fmt.Printf("%v, ", node.Value)
inOrderPrint(node.Right) // inOrderPrint(node.Right)
} // }
func levelOrderTraverse[T any](root *datastructure.TreeNode[T], traversal *[]T) { func levelOrderTraverse[T any](root *datastructure.TreeNode[T], traversal *[]T) {
var q []*datastructure.TreeNode[T] // queue var q []*datastructure.TreeNode[T] // queue

View File

@@ -4,24 +4,24 @@
// Package datetime implements some functions to format date and time. // Package datetime implements some functions to format date and time.
// Note: // Note:
// 1. `format` param in FormatTimeToStr function should be as flow: // 1. `format` param in FormatTimeToStr function should be as flow:
//"yyyy-mm-dd hh:mm:ss" // "yyyy-mm-dd hh:mm:ss"
//"yyyy-mm-dd hh:mm" // "yyyy-mm-dd hh:mm"
//"yyyy-mm-dd hh" // "yyyy-mm-dd hh"
//"yyyy-mm-dd" // "yyyy-mm-dd"
//"yyyy-mm" // "yyyy-mm"
//"mm-dd" // "mm-dd"
//"dd-mm-yy hh:mm:ss" // "dd-mm-yy hh:mm:ss"
//"yyyy/mm/dd hh:mm:ss" // "yyyy/mm/dd hh:mm:ss"
//"yyyy/mm/dd hh:mm" // "yyyy/mm/dd hh:mm"
//"yyyy/mm/dd hh" // "yyyy/mm/dd hh"
//"yyyy/mm/dd" // "yyyy/mm/dd"
//"yyyy/mm" // "yyyy/mm"
//"mm/dd" // "mm/dd"
//"dd/mm/yy hh:mm:ss" // "dd/mm/yy hh:mm:ss"
//"yyyy" // "yyyy"
//"mm" // "mm"
//"hh:mm:ss" // "hh:mm:ss"
//"mm:ss" // "mm:ss"
package datetime package datetime
import ( import (
@@ -147,16 +147,32 @@ func EndOfDay(t time.Time) time.Time {
return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location()) return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location())
} }
// BeginOfWeek return beginning week, week begin from Sunday // BeginOfWeek return beginning week, default week begin from Sunday
func BeginOfWeek(t time.Time) time.Time { func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time {
y, m, d := t.AddDate(0, 0, 0-int(BeginOfDay(t).Weekday())).Date() var beginFromWeekday = time.Sunday
return time.Date(y, m, d, 0, 0, 0, 0, t.Location()) if len(beginFrom) > 0 {
beginFromWeekday = beginFrom[0]
}
y, m, d := t.AddDate(0, 0, int(beginFromWeekday-t.Weekday())).Date()
beginOfWeek := time.Date(y, m, d, 0, 0, 0, 0, t.Location())
if beginOfWeek.After(t) {
return beginOfWeek.AddDate(0, 0, -7)
}
return beginOfWeek
} }
// EndOfWeek return end week time, week end with Saturday // EndOfWeek return end week time, default week end with Saturday
func EndOfWeek(t time.Time) time.Time { func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time {
y, m, d := BeginOfWeek(t).AddDate(0, 0, 7).Add(-time.Nanosecond).Date() var endWithWeekday = time.Saturday
return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location()) if len(endWith) > 0 {
endWithWeekday = endWith[0]
}
y, m, d := t.AddDate(0, 0, int(endWithWeekday-t.Weekday())).Date()
var endWithWeek = time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location())
if endWithWeek.Before(t) {
endWithWeek = endWithWeek.AddDate(0, 0, 7)
}
return endWithWeek
} }
// BeginOfMonth return beginning of month // BeginOfMonth return beginning of month

View File

@@ -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)
@@ -570,6 +570,8 @@ func main() {
func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V]
func (l *LRUCache[K, V]) Get(key K) (V, bool) func (l *LRUCache[K, V]) Get(key K) (V, bool)
func (l *LRUCache[K, V]) Put(key K, value V) func (l *LRUCache[K, V]) Put(key K, value V)
func (l *LRUCache[K, V]) Delete(key K) bool
func (l *LRUCache[K, V]) Len() int
``` ```
<b>Example:</b> <b>Example:</b>
@@ -586,10 +588,14 @@ func main() {
cache.Put(1, 1) cache.Put(1, 1)
cache.Put(2, 2) cache.Put(2, 2)
cache.Put(3, 3)
_, ok := cache.Get(0) // ok -> false fmt.Println(cache.Len()) // 3
v, ok := cache.Get(1) // v->1, ok->true v, ok := cache.Get(1)
fmt.Println(v, ok) // 1 true
ok = cache.Delete(1)
fmt.Println(ok) // true
} }
``` ```

View File

@@ -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)
@@ -571,6 +570,8 @@ func main() {
func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V]
func (l *LRUCache[K, V]) Get(key K) (V, bool) func (l *LRUCache[K, V]) Get(key K) (V, bool)
func (l *LRUCache[K, V]) Put(key K, value V) func (l *LRUCache[K, V]) Put(key K, value V)
func (l *LRUCache[K, V]) Delete(key K) bool
func (l *LRUCache[K, V]) Len() int
``` ```
<b>Example:</b> <b>Example:</b>
@@ -587,10 +588,14 @@ func main() {
cache.Put(1, 1) cache.Put(1, 1)
cache.Put(2, 2) cache.Put(2, 2)
cache.Put(3, 3)
_, ok := cache.Get(0) // ok -> false fmt.Println(cache.Len()) // 3
v, ok := cache.Get(1) // v->1, ok->true v, ok := cache.Get(1)
fmt.Println(v, ok) // 1 true
ok = cache.Delete(1)
fmt.Println(ok) // true
} }
``` ```

View File

@@ -38,13 +38,13 @@ import (
## Channel ## Channel
### <span id="NewChannel">NewChannel</span> ### <span id="NewChannel">NewChannel</span>
<p>return a Channel pointer instance.</p> <p>Create a Channel pointer instance.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
type Channel struct {} type Channel[T any] struct
func NewChannel() *Channel func NewChannel[T any]() *Channel[T]
``` ```
<b>Example:</b> <b>Example:</b>
@@ -57,7 +57,7 @@ import (
) )
func main() { func main() {
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
} }
``` ```
@@ -70,7 +70,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (c *Channel) Bridge(ctx context.Context, chanStream <-chan <-chan any) <-chan any func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -87,25 +87,30 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
genVals := func() <-chan <-chan any { genVals := func() <-chan <-chan int {
chanStream := make(chan (<-chan any)) out := make(chan (<-chan int))
go func() { go func() {
defer close(chanStream) defer close(out)
for i := 0; i < 10; i++ { for i := 1; i <= 5; i++ {
stream := make(chan any, 1) stream := make(chan int, 1)
stream <- i stream <- i
close(stream) close(stream)
chanStream <- stream out <- stream
} }
}() }()
return chanStream return out
} }
index := 0 for v := range c.Bridge(ctx, genVals()) {
for val := range c.Bridge(ctx, genVals()) { fmt.Println(v)
fmt.Printf("%v ", val) //0 1 2 3 4 5 6 7 8 9
} }
// Output:
// 1
// 2
// 3
// 4
// 5
} }
``` ```
@@ -114,12 +119,12 @@ func main() {
### <span id="FanIn">FanIn</span> ### <span id="FanIn">FanIn</span>
<p>merge multiple channels into one channel until cancel the context.</p> <p>Merge multiple channels into one channel until cancel the context.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (c *Channel) FanIn(ctx context.Context, channels ...<-chan any) <-chan any func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -136,17 +141,17 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
channels := make([]<-chan any, 3) channels := make([]<-chan int, 2)
for i := 0; i < 3; i++ { for i := 0; i < 2; i++ {
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 3) channels[i] = c.Take(ctx, c.Repeat(ctx, i), 2)
} }
mergedChannel := c.FanIn(ctx, channels...) chs := c.FanIn(ctx, channels...)
for val := range mergedChannel { for v := range chs {
fmt.Println("\t%d\n", val) //1,2,1,0,0,1,0,2,2 (order not for sure) fmt.Println(v) //1 1 0 0 or 0 0 1 1
} }
} }
``` ```
@@ -154,12 +159,12 @@ func main() {
### <span id="Repeat">Repeat</span> ### <span id="Repeat">Repeat</span>
<p>Return a chan, put param `values` into the chan repeatly until cancel the context.</p> <p>Create channel, put values into the channel repeatly until cancel the context.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (c *Channel) Repeat(ctx context.Context, values ...any) <-chan any func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -176,26 +181,30 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 5) intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4)
for v := range intStream { for v := range intStream {
fmt.Println(v) //1, 2, 1, 2, 1 fmt.Println(v)
} }
// Output:
// 1
// 2
// 1
// 2
} }
``` ```
### <span id="Generate">Generate</span>
### <span id="RepeatFn">RepeatFn</span> <p>Creates a channel, then put values into the channel.</p>
<p>Return a chan, excutes fn repeatly, and put the result into retruned chan until cancel context.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (c *Channel) RepeatFn(ctx context.Context, fn func() any) <-chan any func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -212,16 +221,58 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
fn := func() any { c := concurrency.NewChannel[int]()
s := "a" intStream := c.Generate(ctx, 1, 2, 3)
return s
}
c := concurrency.NewChannel()
dataStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
for v := range dataStream { fmt.Println(<-intStream)
fmt.Println(v) //a, a, a fmt.Println(<-intStream)
fmt.Println(<-intStream)
// Output:
// 1
// 2
// 3
}
```
### <span id="RepeatFn">RepeatFn</span>
<p>Create a channel, excutes fn repeatly, and put the result into the channel, until close context.</p>
<b>Signature:</b>
```go
func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T
```
<b>Example:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fn := func() string {
return "hello"
} }
c := concurrency.NewChannel[string]()
intStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
for v := range intStream {
fmt.Println(v)
}
// Output:
// hello
// hello
// hello
} }
``` ```
@@ -234,7 +285,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (c *Channel) Or(channels ...<-chan any) <-chan any func (c *Channel[T]) Or(channels ...<-chan T) <-chan T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -249,7 +300,7 @@ import (
func main() { func main() {
sig := func(after time.Duration) <-chan any { sig := func(after time.Duration) <-chan any {
c := make(chan interface{}) c := make(chan any)
go func() { go func() {
defer close(c) defer close(c)
time.Sleep(after) time.Sleep(after)
@@ -259,13 +310,11 @@ func main() {
start := time.Now() start := time.Now()
c := concurrency.NewChannel() c := concurrency.NewChannel[any]()
<-c.Or( <-c.Or(
sig(1*time.Second), sig(1*time.Second),
sig(2*time.Second), sig(2*time.Second),
sig(3*time.Second), sig(3*time.Second),
sig(4*time.Second),
sig(5*time.Second),
) )
fmt.Println("done after %v", time.Since(start)) //1.003s fmt.Println("done after %v", time.Since(start)) //1.003s
@@ -282,7 +331,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (c *Channel) OrDone(ctx context.Context, channel <-chan any) <-chan any func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -299,12 +348,16 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3) intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
for val := range c.OrDone(ctx, intStream) { for v := range c.OrDone(ctx, intStream) {
fmt.Println(val) //1 fmt.Println(v)
} }
// Output:
// 1
// 1
// 1
} }
``` ```
@@ -313,12 +366,12 @@ func main() {
### <span id="Take">Take</span> ### <span id="Take">Take</span>
<p>Return a chan whose values are tahken from another chan until cancel context.</p> <p>Create a channel whose values are taken from another channel with limit number.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (c *Channel) Take(ctx context.Context, valueStream <-chan any, number int) <-chan any func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -335,7 +388,7 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
numbers := make(chan any, 5) numbers := make(chan int, 5)
numbers <- 1 numbers <- 1
numbers <- 2 numbers <- 2
numbers <- 3 numbers <- 3
@@ -343,12 +396,16 @@ func main() {
numbers <- 5 numbers <- 5
defer close(numbers) defer close(numbers)
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, numbers, 3) intStream := c.Take(ctx, numbers, 3)
for val := range intStream { for v := range intStream {
fmt.Println(val) //1, 2, 3 fmt.Println(v)
} }
// Output:
// 1
// 2
// 3
} }
``` ```
@@ -356,12 +413,12 @@ func main() {
### <span id="Tee">Tee</span> ### <span id="Tee">Tee</span>
<p>Split one chanel into two channels until cancel context.</p> <p>Split one chanel into two channels, until cancel the context.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (c *Channel) Tee(ctx context.Context, in <-chan any) (<-chan any, <-chan any) func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -378,13 +435,19 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
inStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4) intStream := c.Take(ctx, c.Repeat(ctx, 1), 2)
out1, out2 := c.Tee(ctx, inStream) ch1, ch2 := c.Tee(ctx, intStream)
for val := range out1 {
fmt.Println(val) //1 for v := range ch1 {
fmt.Println(<-out2) //1 fmt.Println(v)
fmt.Println(<-ch2)
} }
// Output:
// 1
// 1
// 1
// 1
} }
``` ```

View File

@@ -38,13 +38,13 @@ import (
### Channel ### Channel
### <span id="NewChannel">NewChannel</span> ### <span id="NewChannel">NewChannel</span>
<p>返回一个 Channel 指针实例</p> <p>返回一个Channel指针实例</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
type Channel struct {} type Channel[T any] struct
func NewChannel() *Channel func NewChannel[T any]() *Channel[T]
``` ```
<b>例子:</b> <b>例子:</b>
@@ -57,7 +57,7 @@ import (
) )
func main() { func main() {
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
} }
``` ```
@@ -65,12 +65,12 @@ func main() {
### <span id="Bridge">Bridge</span> ### <span id="Bridge">Bridge</span>
<p>将多个通道链接到一个通道,直到取消上下文。</p> <p>将多个channel链接到一个channel,直到取消上下文。</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (c *Channel) Bridge(ctx context.Context, chanStream <-chan <-chan any) <-chan any func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -84,28 +84,33 @@ import (
) )
func main() { func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
genVals := func() <-chan <-chan any { genVals := func() <-chan <-chan int {
chanStream := make(chan (<-chan any)) out := make(chan (<-chan int))
go func() { go func() {
defer close(chanStream) defer close(out)
for i := 0; i < 10; i++ { for i := 1; i <= 5; i++ {
stream := make(chan any, 1) stream := make(chan int, 1)
stream <- i stream <- i
close(stream) close(stream)
chanStream <- stream out <- stream
} }
}() }()
return chanStream return out
} }
index := 0 for v := range c.Bridge(ctx, genVals()) {
for val := range c.Bridge(ctx, genVals()) { fmt.Println(v)
fmt.Printf("%v ", val) //0 1 2 3 4 5 6 7 8 9
} }
// Output:
// 1
// 2
// 3
// 4
// 5
} }
``` ```
@@ -114,12 +119,12 @@ func main() {
### <span id="FanIn">FanIn</span> ### <span id="FanIn">FanIn</span>
<p>将多个通道合并为一个通道,直到取消上下文</p> <p>将多个channel合并为一个channel,直到取消上下文</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (c *Channel) FanIn(ctx context.Context, channels ...<-chan any) <-chan any func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -133,33 +138,33 @@ import (
) )
func main() { func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
channels := make([]<-chan any, 3) channels := make([]<-chan int, 2)
for i := 0; i < 3; i++ { for i := 0; i < 2; i++ {
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 3) channels[i] = c.Take(ctx, c.Repeat(ctx, i), 2)
} }
mergedChannel := c.FanIn(ctx, channels...) chs := c.FanIn(ctx, channels...)
for val := range mergedChannel { for v := range chs {
fmt.Println("\t%d\n", val) //1,2,1,0,0,1,0,2,2 (order not for sure) fmt.Println(v) //1 1 0 0 or 0 0 1 1
} }
} }
``` ```
### <span id="Repeat">Repeat</span> ### <span id="Generate">Generate</span>
<p>返回一个chan将参数`values`重复放入chan直到取消上下文。</p> <p>根据传入的值生成channel.</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (c *Channel) Repeat(ctx context.Context, values ...any) <-chan any func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -173,15 +178,58 @@ import (
) )
func main() { func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 5) intStream := c.Generate(ctx, 1, 2, 3)
fmt.Println(<-intStream)
fmt.Println(<-intStream)
fmt.Println(<-intStream)
// Output:
// 1
// 2
// 3
}
```
### <span id="Repeat">Repeat</span>
<p>返回一个channel将参数`values`重复放入channel直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T
```
<b>例子:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4)
for v := range intStream { for v := range intStream {
fmt.Println(v) //1, 2, 1, 2, 1 fmt.Println(v)
} }
// Output:
// 1
// 2
// 1
// 2
} }
``` ```
@@ -190,12 +238,12 @@ func main() {
### <span id="RepeatFn">RepeatFn</span> ### <span id="RepeatFn">RepeatFn</span>
<p>返回一个chan重复执行函数fn并将结果放入返回的chan直到取消上下文。</p> <p>返回一个channel重复执行函数fn并将结果放入返回的channel,直到取消上下文。</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (c *Channel) RepeatFn(ctx context.Context, fn func() any) <-chan any func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -209,19 +257,23 @@ import (
) )
func main() { func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
fn := func() any { fn := func() string {
s := "a" return "hello"
return s
} }
c := concurrency.NewChannel()
dataStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
for v := range dataStream { c := concurrency.NewChannel[string]()
fmt.Println(v) //a, a, a intStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
for v := range intStream {
fmt.Println(v)
} }
// Output:
// hello
// hello
// hello
} }
``` ```
@@ -229,12 +281,12 @@ func main() {
### <span id="Or">Or</span> ### <span id="Or">Or</span>
<p>将一个或多个通道读取到一个通道中,当任何读取通道关闭时将结束读取。</p> <p>将一个或多个channel读取到一个channel中当任何读取channel关闭时将结束读取。</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (c *Channel) Or(channels ...<-chan any) <-chan any func (c *Channel[T]) Or(channels ...<-chan T) <-chan T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -249,7 +301,7 @@ import (
func main() { func main() {
sig := func(after time.Duration) <-chan any { sig := func(after time.Duration) <-chan any {
c := make(chan interface{}) c := make(chan any)
go func() { go func() {
defer close(c) defer close(c)
time.Sleep(after) time.Sleep(after)
@@ -259,13 +311,11 @@ func main() {
start := time.Now() start := time.Now()
c := concurrency.NewChannel() c := concurrency.NewChannel[any]()
<-c.Or( <-c.Or(
sig(1*time.Second), sig(1*time.Second),
sig(2*time.Second), sig(2*time.Second),
sig(3*time.Second), sig(3*time.Second),
sig(4*time.Second),
sig(5*time.Second),
) )
fmt.Println("done after %v", time.Since(start)) //1.003s fmt.Println("done after %v", time.Since(start)) //1.003s
@@ -277,12 +327,12 @@ func main() {
### <span id="OrDone">OrDone</span> ### <span id="OrDone">OrDone</span>
<p>将一个通道读入另一个通道,直到取消上下文。</p> <p>将一个channel读入另一个channel,直到取消上下文。</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (c *Channel) OrDone(ctx context.Context, channel <-chan any) <-chan any func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -299,12 +349,16 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3) intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
for val := range c.OrDone(ctx, intStream) { for v := range c.OrDone(ctx, intStream) {
fmt.Println(val) //1 fmt.Println(v)
} }
// Output:
// 1
// 1
// 1
} }
``` ```
@@ -313,12 +367,12 @@ func main() {
### <span id="Take">Take</span> ### <span id="Take">Take</span>
<p>返回一个chan其值从另一个chan获取直到取消上下文。</p> <p>返回一个channel其值从另一个channel获取,直到取消上下文。</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (c *Channel) Take(ctx context.Context, valueStream <-chan any, number int) <-chan any func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -335,7 +389,7 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
numbers := make(chan any, 5) numbers := make(chan int, 5)
numbers <- 1 numbers <- 1
numbers <- 2 numbers <- 2
numbers <- 3 numbers <- 3
@@ -343,12 +397,16 @@ func main() {
numbers <- 5 numbers <- 5
defer close(numbers) defer close(numbers)
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, numbers, 3) intStream := c.Take(ctx, numbers, 3)
for val := range intStream { for v := range intStream {
fmt.Println(val) //1, 2, 3 fmt.Println(v)
} }
// Output:
// 1
// 2
// 3
} }
``` ```
@@ -356,12 +414,12 @@ func main() {
### <span id="Tee">Tee</span> ### <span id="Tee">Tee</span>
<p>将一个通道分成两个通道,直到取消上下文。</p> <p>将一个channel分成两个channel,直到取消上下文。</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (c *Channel) Tee(ctx context.Context, in <-chan any) (<-chan any, <-chan any) func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T)
``` ```
<b>例子:</b> <b>例子:</b>
@@ -378,13 +436,19 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
c := concurrency.NewChannel() c := concurrency.NewChannel[int]()
inStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4) intStream := c.Take(ctx, c.Repeat(ctx, 1), 2)
out1, out2 := c.Tee(ctx, inStream) ch1, ch2 := c.Tee(ctx, intStream)
for val := range out1 {
fmt.Println(val) //1 for v := range ch1 {
fmt.Println(<-out2) //1 fmt.Println(v)
fmt.Println(<-ch2)
} }
// Output:
// 1
// 1
// 1
// 1
} }
``` ```

View File

@@ -25,6 +25,7 @@ import (
- [Or](#Or) - [Or](#Or)
- [Xor](#Generate) - [Xor](#Generate)
- [Nor](#Nor) - [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand) - [Nand](#Nand)
- [TernaryOperator](#TernaryOperator) - [TernaryOperator](#TernaryOperator)
@@ -120,7 +121,7 @@ func main() {
### <span id="Or">Or</span> ### <span id="Or">Or</span>
<p>Returns false iff neither a nor b is truthy.</p> <p>Returns false if neither a nor b is truthy.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -148,7 +149,7 @@ func main() {
### <span id="Xor">Xor</span> ### <span id="Xor">Xor</span>
<p>Returns true iff a or b but not both is truthy.</p> <p>Returns true if a or b but not both is truthy.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -176,7 +177,7 @@ func main() {
### <span id="Nor">Nor</span> ### <span id="Nor">Nor</span>
<p>Returns true iff neither a nor b is truthy.</p> <p>Returns true if neither a nor b is truthy.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -197,14 +198,40 @@ func main() {
fmt.Println(condition.Nor(0, 0)) // true fmt.Println(condition.Nor(0, 0)) // true
fmt.Println(condition.Nor(0, 1)) // false fmt.Println(condition.Nor(0, 1)) // false
fmt.Println(condition.Nor(1, 0)) // false fmt.Println(condition.Nor(1, 0)) // false
fmt.Println(condition.Nor(1, 1)) // true fmt.Println(condition.Nor(1, 1)) // false
} }
``` ```
### <span id="Xnor">Xnor</span>
<p>Returns true if both a and b or neither a nor b are truthy.</p>
<b>Signature:</b>
```go
func Xnor[T, U any](a T, b U) bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
fmt.Println(condition.Xnor(0, 0)) // true
fmt.Println(condition.Xnor(0, 1)) // false
fmt.Println(condition.Xnor(1, 0)) // false
fmt.Println(condition.Xnor(1, 1)) // true
}
```
### <span id="Nand">Nand</span> ### <span id="Nand">Nand</span>
<p>Returns false iff both a and b are truthy</p> <p>Returns false if both a and b are truthy</p>
<b>Signature:</b> <b>Signature:</b>

View File

@@ -25,6 +25,7 @@ import (
- [Or](#Or) - [Or](#Or)
- [Xor](#Generate) - [Xor](#Generate)
- [Nor](#Nor) - [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand) - [Nand](#Nand)
- [TernaryOperator](#TernaryOperator) - [TernaryOperator](#TernaryOperator)
@@ -196,12 +197,38 @@ func main() {
fmt.Println(condition.Nor(0, 0)) // true fmt.Println(condition.Nor(0, 0)) // true
fmt.Println(condition.Nor(0, 1)) // false fmt.Println(condition.Nor(0, 1)) // false
fmt.Println(condition.Nor(1, 0)) // false fmt.Println(condition.Nor(1, 0)) // false
fmt.Println(condition.Nor(1, 1)) // true fmt.Println(condition.Nor(1, 1)) // false
} }
``` ```
### <span id="Xnor">Xnor</span>
<p>如果a和b都是真的或a和b均是假的则返回true。</p>
<b>函数签名:</b>
```go
func Xnor[T, U any](a T, b U) bool
```
<b>例子:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
fmt.Println(condition.Xnor(0, 0)) // true
fmt.Println(condition.Xnor(0, 1)) // false
fmt.Println(condition.Xnor(1, 0)) // false
fmt.Println(condition.Xnor(1, 1)) // true
}
```
### <span id="Nand">Nand</span> ### <span id="Nand">Nand</span>
<p>如果a和b都为真返回false否则返回true</p> <p>如果a和b都为真返回false否则返回true</p>

View File

@@ -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)
@@ -103,7 +102,7 @@ func main() {
### <span id="ToBool">ToBool</span> ### <span id="ToBool">ToBool</span>
<p>Convert string to a boolean value. Use strconv.ParseBool</p> <p>Convert string to bool. Use strconv.ParseBool.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -139,7 +138,7 @@ func main() {
### <span id="ToBytes">ToBytes</span> ### <span id="ToBytes">ToBytes</span>
<p>Convert interface to byte slice.</p> <p>Convert value to byte slice.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -201,7 +200,7 @@ func main() {
### <span id="ToChannel">ToChannel</span> ### <span id="ToChannel">ToChannel</span>
<p>Convert a collection of elements to a read-only channels.</p> <p>Convert a collection of elements to a read-only channel.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -239,7 +238,7 @@ func main() {
### <span id="ToFloat">ToFloat</span> ### <span id="ToFloat">ToFloat</span>
<p>Convert interface to a float64 value. If param is a invalid floatable, will return 0 and error. </p> <p>Convert value to a float64 value. If param is a invalid floatable, will return 0.0 and error. </p>
<b>Signature:</b> <b>Signature:</b>
@@ -272,7 +271,7 @@ func main() {
### <span id="ToInt">ToInt</span> ### <span id="ToInt">ToInt</span>
<p>Convert interface to a int64 value. If param is a invalid intable, will return 0 and error. </p> <p>Convert value to a int64 value. If param is a invalid intable, will return 0 and error. </p>
<b>Signature:</b> <b>Signature:</b>
@@ -333,7 +332,7 @@ func main() {
### <span id="ToMap">ToMap</span> ### <span id="ToMap">ToMap</span>
<p>Convert a slice or an array of structs to a map based on iteratee function. </p> <p>Convert a slice of structs to a map based on iteratee function. </p>
<b>Signature:</b> <b>Signature:</b>
@@ -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> ### <span id="StructToMap">StructToMap</span>

View File

@@ -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)
@@ -401,7 +400,7 @@ func main() {
### <span id="ToString">ToString</span> ### <span id="ToString">ToString</span>
<p>将interface转成字符串</p> <p>将值转换为字符串,对于数字、字符串、[]byte将转换为字符串。 对于其他类型(切片、映射、数组、结构)将调用 json.Marshal</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -523,7 +522,7 @@ func main() {
### <span id="DecodeByte">DecodeByte</span> ### <span id="DecodeByte">DecodeByte</span>
<p>解码字节切片到目标对象,目标对象需要传入一个指针实例</p> <p>解码字节切片到目标对象,目标对象需要传入一个指针实例</p>
<b>函数签名:</b> <b>函数签名:</b>

View File

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

View File

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

View File

@@ -29,6 +29,9 @@ import (
- [Put](#Put) - [Put](#Put)
- [Delete](#Delete) - [Delete](#Delete)
- [Contains](#Contains) - [Contains](#Contains)
- [Iterate](#Iterate)
- [Keys](#Keys)
- [Values](#Values)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -207,3 +210,105 @@ func main() {
fmt.Println(hm.Contains("b")) //false fmt.Println(hm.Contains("b")) //false
} }
``` ```
### <span id="Iterate">Iterate</span>
<p>Executes iteratee funcation for every key and value pair of hashmap.</p>
<b>Signature:</b>
```go
func (hm *HashMap) Iterate(iteratee func(key, value any))
```
<b>Example:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
hm.Iterate(func(key, value any) {
fmt.Println(key)
fmt.Println(value)
})
}
```
### <span id="Keys">Keys</span>
<p>Return a slice of the hashmap's keys (random order).</p>
<b>Signature:</b>
```go
func (hm *HashMap) Keys() []any
```
<b>Example:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
keys := hm.Keys()
fmt.Println(keys) //[]interface{"a", "b", "c"}
}
```
### <span id="Values">Values</span>
<p>Return a slice of the hashmap's values (random order).</p>
<b>Signature:</b>
```go
func (hm *HashMap) Values() []any
```
<b>Example:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
values := hm.Values()
fmt.Println(values) //[]interface{2, 1, 3}
}
```

View File

@@ -24,11 +24,13 @@ import (
- [NewHashMap](#NewHashMap) - [NewHashMap](#NewHashMap)
- [NewHashMapWithCapacity](#NewHashMapWithCapacity) - [NewHashMapWithCapacity](#NewHashMapWithCapacity)
- [Get](#Get) - [Get](#Get)
- [Put](#Put) - [Put](#Put)
- [Delete](#Delete) - [Delete](#Delete)
- [Contains](#Contains) - [Contains](#Contains)
- [Iterate](#Iterate)
- [Keys](#Keys)
- [Values](#Values)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -203,3 +205,104 @@ func main() {
fmt.Println(hm.Contains("b")) //false fmt.Println(hm.Contains("b")) //false
} }
``` ```
### <span id="Iterate">Iterate</span>
<p>迭代hashmap对每个key和value执行iteratee函数</p>
<b>函数签名:</b>
```go
func (hm *HashMap) Iterate(iteratee func(key, value any))
```
<b>例子:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
hm.Iterate(func(key, value any) {
fmt.Println(key)
fmt.Println(value)
})
}
```
### <span id="Keys">Keys</span>
<p>返回hashmap所有key的切片 (随机顺序)</p>
<b>函数签名:</b>
```go
func (hm *HashMap) Keys() []any
```
<b>例子:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
keys := hm.Keys()
fmt.Println(keys) //[]interface{"a", "b", "c"}
}
```
### <span id="Values">Values</span>
<p>返回hashmap所有值的切片 (随机顺序).</p>
<b>函数签名:</b>
```go
func (hm *HashMap) Values() []any
```
<b>例子:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
values := hm.Values()
fmt.Println(values) //[]interface{2, 1, 3}
}
```

View File

@@ -132,12 +132,12 @@ func main() {
### <span id="SinglyLink_InsertAt">InsertAt</span> ### <span id="SinglyLink_InsertAt">InsertAt</span>
<p>Insert value into singly linklist at index, index shoud be great or equal 0 and less or equal number of link nodes</p> <p>Insert value into singly linklist at index, param `index` should between [0, len(SinglyLink)], if index do not meet the conditions, do nothing</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *SinglyLink[T]) InsertAt(index int, value T) error func (link *SinglyLink[T]) InsertAt(index int, value T)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -152,6 +152,8 @@ import (
func main() { func main() {
lk := link.NewSinglyLink[int]() lk := link.NewSinglyLink[int]()
lk.InsertAt(1, 1) //do nothing
lk.InsertAt(0, 1) lk.InsertAt(0, 1)
lk.InsertAt(1, 2) lk.InsertAt(1, 2)
lk.InsertAt(2, 3) lk.InsertAt(2, 3)
@@ -228,12 +230,12 @@ func main() {
### <span id="SinglyLink_DeleteAt">DeleteAt</span> ### <span id="SinglyLink_DeleteAt">DeleteAt</span>
<p>Delete value at specific index, index shoud be great or equal 0 and less or less than number of link nodes - 1</p> <p>Delete value at specific index, param `index` should be [0, len(SinglyLink)-1]</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAt(index int) error func (link *SinglyLink[T]) DeleteAt(index int)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -253,9 +255,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAt(3) lk.DeleteAt(3)
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2, 3} fmt.Println(lk.Values()) //[]int{1, 2, 3}
} }
``` ```
@@ -268,7 +269,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAtHead() error func (link *SinglyLink[T]) DeleteAtHead()
``` ```
<b>Example:</b> <b>Example:</b>
@@ -288,9 +289,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAtHead() lk.DeleteAtHead()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{2, 3, 4} fmt.Println(lk.Values()) //[]int{2, 3, 4}
} }
``` ```
@@ -304,7 +304,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAtTail() error func (link *SinglyLink[T]) DeleteAtTail()
``` ```
<b>Example:</b> <b>Example:</b>
@@ -323,9 +323,8 @@ func main() {
lk.InsertAtTail(2) lk.InsertAtTail(2)
lk.InsertAtTail(3) lk.InsertAtTail(3)
err := lk.DeleteAtTail() lk.DeleteAtTail()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2} fmt.Println(lk.Values()) //[]int{1, 2}
} }
``` ```
@@ -628,12 +627,12 @@ func main() {
### <span id="DoublyLink_InsertAt">InsertAt</span> ### <span id="DoublyLink_InsertAt">InsertAt</span>
<p>Insert value into doubly linklist at index, index shoud be great or equal 0 and less or equal number of link nodes</p> <p>Insert value into doubly linklist at index, param `index` should between [0, len(DoublyLink)], if index do not meet the conditions, do nothing</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *DoublyLink[T]) InsertAt(index int, value T) error func (link *DoublyLink[T]) InsertAt(index int, value T)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -648,6 +647,8 @@ import (
func main() { func main() {
lk := link.NewDoublyLink[int]() lk := link.NewDoublyLink[int]()
lk.InsertAt(1, 1) //do nothing
lk.InsertAt(0, 1) lk.InsertAt(0, 1)
lk.InsertAt(1, 2) lk.InsertAt(1, 2)
lk.InsertAt(2, 3) lk.InsertAt(2, 3)
@@ -724,12 +725,12 @@ func main() {
### <span id="DoublyLink_DeleteAt">DeleteAt</span> ### <span id="DoublyLink_DeleteAt">DeleteAt</span>
<p>Delete value at specific index, index shoud be great or equal 0 and less or less than number of link nodes - 1</p> <p>Delete value at specific index, param `index` should be [0, len(DoublyLink)-1]</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *DoublyLink[T]) DeleteAt(index int) error func (link *DoublyLink[T]) DeleteAt(index int)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -749,9 +750,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAt(3) lk.DeleteAt(3)
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2, 3} fmt.Println(lk.Values()) //[]int{1, 2, 3}
} }
``` ```
@@ -764,7 +764,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *DoublyLink[T]) DeleteAtHead() error func (link *DoublyLink[T]) DeleteAtHead()
``` ```
<b>Example:</b> <b>Example:</b>
@@ -784,9 +784,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAtHead() lk.DeleteAtHead()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{2, 3, 4} fmt.Println(lk.Values()) //[]int{2, 3, 4}
} }
``` ```

View File

@@ -132,12 +132,12 @@ func main() {
### <span id="SinglyLink_InsertAt">InsertAt</span> ### <span id="SinglyLink_InsertAt">InsertAt</span>
<p>将值插入到索引处的链表中,索引应大于或等于 0 且小于或等于链表节点数</p> <p>将值插入到索引处的链表中,索引应大于或等于0且小于或等于链表节点数</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *SinglyLink[T]) InsertAt(index int, value T) error func (link *SinglyLink[T]) InsertAt(index int, value T)
``` ```
<b>例子:</b> <b>例子:</b>
@@ -152,6 +152,8 @@ import (
func main() { func main() {
lk := link.NewSinglyLink[int]() lk := link.NewSinglyLink[int]()
lk.InsertAt(1, 1) //do nothing
lk.InsertAt(0, 1) lk.InsertAt(0, 1)
lk.InsertAt(1, 2) lk.InsertAt(1, 2)
lk.InsertAt(2, 3) lk.InsertAt(2, 3)
@@ -228,12 +230,12 @@ func main() {
### <span id="SinglyLink_DeleteAt">DeleteAt</span> ### <span id="SinglyLink_DeleteAt">DeleteAt</span>
<p>删除特定索引处的值索引应大于或等于0且小于或等于链接节点数 - 1</p> <p>删除特定索引处的值索引应大于或等于0且小于或等于链接节点数-1</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAt(index int) error func (link *SinglyLink[T]) DeleteAt(index int)
``` ```
<b>例子:</b> <b>例子:</b>
@@ -253,9 +255,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAt(3) lk.DeleteAt(3)
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2, 3} fmt.Println(lk.Values()) //[]int{1, 2, 3}
} }
``` ```
@@ -268,7 +269,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAtHead() error func (link *SinglyLink[T]) DeleteAtHead()
``` ```
<b>例子:</b> <b>例子:</b>
@@ -288,9 +289,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAtHead() lk.DeleteAtHead()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{2, 3, 4} fmt.Println(lk.Values()) //[]int{2, 3, 4}
} }
``` ```
@@ -304,7 +304,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAtTail() error func (link *SinglyLink[T]) DeleteAtTail()
``` ```
<b>例子:</b> <b>例子:</b>
@@ -323,9 +323,8 @@ func main() {
lk.InsertAtTail(2) lk.InsertAtTail(2)
lk.InsertAtTail(3) lk.InsertAtTail(3)
err := lk.DeleteAtTail() lk.DeleteAtTail()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2} fmt.Println(lk.Values()) //[]int{1, 2}
} }
``` ```
@@ -628,12 +627,12 @@ func main() {
### <span id="DoublyLink_InsertAt">InsertAt</span> ### <span id="DoublyLink_InsertAt">InsertAt</span>
<p>将值插入到索引处的链表中,索引应大于或等于 0 且小于或等于链表节点数</p> <p>将值插入到索引处的链表中,索引应大于或等于0且小于或等于链表节点数</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *DoublyLink[T]) InsertAt(index int, value T) error func (link *DoublyLink[T]) InsertAt(index int, value T)
``` ```
<b>例子:</b> <b>例子:</b>
@@ -648,6 +647,8 @@ import (
func main() { func main() {
lk := link.NewDoublyLink[int]() lk := link.NewDoublyLink[int]()
lk.InsertAt(1, 1) //do nothing
lk.InsertAt(0, 1) lk.InsertAt(0, 1)
lk.InsertAt(1, 2) lk.InsertAt(1, 2)
lk.InsertAt(2, 3) lk.InsertAt(2, 3)
@@ -724,12 +725,12 @@ func main() {
### <span id="DoublyLink_DeleteAt">DeleteAt</span> ### <span id="DoublyLink_DeleteAt">DeleteAt</span>
<p>删除特定索引处的值索引应大于或等于0且小于或等于链接节点数 - 1</p> <p>删除特定索引处的值索引应大于或等于0且小于或等于链接节点数-1</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *DoublyLink[T]) DeleteAt(index int) error func (link *DoublyLink[T]) DeleteAt(index int)
``` ```
<b>例子:</b> <b>例子:</b>
@@ -749,9 +750,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAt(3) lk.DeleteAt(3)
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2, 3} fmt.Println(lk.Values()) //[]int{1, 2, 3}
} }
``` ```
@@ -764,7 +764,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *DoublyLink[T]) DeleteAtHead() error func (link *DoublyLink[T]) DeleteAtHead()
``` ```
<b>例子:</b> <b>例子:</b>
@@ -784,9 +784,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAtHead() lk.DeleteAtHead()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{2, 3, 4} fmt.Println(lk.Values()) //[]int{2, 3, 4}
} }
``` ```
@@ -800,7 +799,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *DoublyLink[T]) DeleteAtTail() error func (link *DoublyLink[T]) DeleteAtTail()
``` ```
<b>例子:</b> <b>例子:</b>
@@ -819,9 +818,8 @@ func main() {
lk.InsertAtTail(2) lk.InsertAtTail(2)
lk.InsertAtTail(3) lk.InsertAtTail(3)
err := lk.DeleteAtTail() lk.DeleteAtTail()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2} fmt.Println(lk.Values()) //[]int{1, 2}
} }
``` ```

View File

@@ -22,8 +22,11 @@ import (
## Index ## Index
- [NewSet](#NewSet) - [NewSet](#NewSet)
- [NewSetFromSlice](#NewSetFromSlice)
- [Values](#Values) - [Values](#Values)
- [Add](#Add) - [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
- [AddIfNotExistBy](#AddIfNotExistBy)
- [Delete](#Delete) - [Delete](#Delete)
- [Contain](#Contain) - [Contain](#Contain)
- [ContainAll](#ContainAll) - [ContainAll](#ContainAll)
@@ -34,7 +37,6 @@ import (
- [IsEmpty](#IsEmpty) - [IsEmpty](#IsEmpty)
- [Union](#Union) - [Union](#Union)
- [Intersection](#Intersection) - [Intersection](#Intersection)
- [SymmetricDifference](#SymmetricDifference) - [SymmetricDifference](#SymmetricDifference)
- [Minus](#Minus) - [Minus](#Minus)
@@ -45,13 +47,13 @@ import (
## Documentation ## Documentation
### <span id="NewSet">NewSet</span> ### <span id="NewSet">NewSet</span>
<p>Make a Set instance</p> <p>Create a set instance</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
type Set[T comparable] map[T]bool 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> <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> ### <span id="Values">Values</span>
@@ -100,12 +126,12 @@ func main() {
### <span id="Add">Add</span> ### <span id="Add">Add</span>
<p>Add value to set</p> <p>Add items to set</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (s Set[T]) Add(values ...T) func (s Set[T]) Add(items ...T)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -126,14 +152,83 @@ func main() {
``` ```
### <span id="AddIfNotExist">AddIfNotExist</span>
### <span id="Delete">Delete</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>
<p>Delete value in set</p>
<b>Signature:</b> <b>Signature:</b>
```go ```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> <b>Example:</b>
@@ -157,12 +252,12 @@ func main() {
### <span id="Contain">Contain</span> ### <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> <b>Signature:</b>
```go ```go
func (s Set[T]) Contain(value T) bool func (s Set[T]) Contain(item T) bool
``` ```
<b>Example:</b> <b>Example:</b>
@@ -309,7 +404,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (s Set[T]) Iterate(fn func(value T)) func (s Set[T]) Iterate(fn func(item T))
``` ```
<b>Example:</b> <b>Example:</b>
@@ -324,8 +419,8 @@ import (
func main() { func main() {
set1 := set.NewSet(1, 2, 3) set1 := set.NewSet(1, 2, 3)
arr := []int{} arr := []int{}
set.Iterate(func(value int) { set.Iterate(func(item int) {
arr = append(arr, value) arr = append(arr, item)
}) })
fmt.Println(arr) //1,2,3 fmt.Println(arr) //1,2,3

View File

@@ -22,8 +22,11 @@ import (
## 目录 ## 目录
- [NewSet](#NewSet) - [NewSet](#NewSet)
- [NewSetFromSlice](#NewSetFromSlice)
- [Values](#Values) - [Values](#Values)
- [Add](#Add) - [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
- [AddIfNotExistBy](#AddIfNotExistBy)
- [Delete](#Delete) - [Delete](#Delete)
- [Contain](#Contain) - [Contain](#Contain)
- [ContainAll](#ContainAll) - [ContainAll](#ContainAll)
@@ -34,7 +37,6 @@ import (
- [IsEmpty](#IsEmpty) - [IsEmpty](#IsEmpty)
- [Union](#Union) - [Union](#Union)
- [Intersection](#Intersection) - [Intersection](#Intersection)
- [SymmetricDifference](#SymmetricDifference) - [SymmetricDifference](#SymmetricDifference)
- [Minus](#Minus) - [Minus](#Minus)
@@ -51,7 +53,7 @@ import (
```go ```go
type Set[T comparable] map[T]bool 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> <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> ### <span id="Values">Values</span>
<p>获取集合中所有元素的切片</p> <p>获取集合中所有元素的切片</p>
@@ -105,7 +132,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (s Set[T]) Add(values ...T) func (s Set[T]) Add(items ...T)
``` ```
<b>例子:</b> <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> ### <span id="Delete">Delete</span>
<p>删除集合中元素</p> <p>删除集合中元素</p>
@@ -133,7 +230,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (s Set[T]) Delete(values ...T) func (s Set[T]) Delete(items ...T)
``` ```
<b>例子:</b> <b>例子:</b>
@@ -162,7 +259,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (s Set[T]) Contain(value T) bool func (s Set[T]) Contain(item T) bool
``` ```
<b>例子:</b> <b>例子:</b>
@@ -309,7 +406,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (s Set[T]) Iterate(fn func(value T)) func (s Set[T]) Iterate(fn func(item T))
``` ```
<b>例子:</b> <b>例子:</b>
@@ -324,8 +421,8 @@ import (
func main() { func main() {
set1 := set.NewSet(1, 2, 3) set1 := set.NewSet(1, 2, 3)
arr := []int{} arr := []int{}
set.Iterate(func(value int) { set.Iterate(func(item int) {
arr = append(arr, value) arr = append(arr, item)
}) })
fmt.Println(arr) //1,2,3 fmt.Println(arr) //1,2,3
@@ -420,9 +517,6 @@ func main() {
``` ```
### <span id="SymmetricDifference">SymmetricDifference</span> ### <span id="SymmetricDifference">SymmetricDifference</span>
<p>返回一个集合,其中元素在第一个集合或第二个集合中,且不同时存在于两个集合中</p> <p>返回一个集合,其中元素在第一个集合或第二个集合中,且不同时存在于两个集合中</p>

View File

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

View File

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

View File

@@ -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)
@@ -251,7 +250,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func BeginOfWeek(t time.Time) time.Time func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time
``` ```
<b>Example:</b> <b>Example:</b>
@@ -414,7 +413,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func EndOfWeek(t time.Time) time.Time func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time
``` ```
<b>Example:</b> <b>Example:</b>

View File

@@ -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)
@@ -243,12 +242,12 @@ func main() {
### <span id="BeginOfWeek">BeginOfWeek</span> ### <span id="BeginOfWeek">BeginOfWeek</span>
<p>返回指定时间的星期开始时间</p> <p>返回指定时间的每周开始时间,默认开始时间星期日</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func BeginOfWeek(t time.Time) time.Time func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time
``` ```
<b>例子:</b> <b>例子:</b>
@@ -406,12 +405,12 @@ func main() {
### <span id="EndOfWeek">EndOfWeek</span> ### <span id="EndOfWeek">EndOfWeek</span>
<p>返回指定时间的星期结束时间</p> <p>返回指定时间的星期结束时间,默认结束时间星期六</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func EndOfWeek(t time.Time) time.Time func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time
``` ```
<b>例子:</b> <b>例子:</b>

View File

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

View File

@@ -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)
@@ -120,7 +119,7 @@ func main() {
### <span id="CopyFile">CopyFile</span> ### <span id="CopyFile">CopyFile</span>
<p>拷贝文件,会覆盖原有的拷贝文件</p> <p>拷贝文件,会覆盖原有的文件</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -262,7 +261,7 @@ func main() {
### <span id="IsDir">IsDir</span> ### <span id="IsDir">IsDir</span>
<p>判断目录是否存在</p> <p>判断参数是否是目录</p>
<b>函数签名:</b> <b>函数签名:</b>

View File

@@ -28,13 +28,12 @@ import (
### <span id="Comma">Comma</span> ### <span id="Comma">Comma</span>
<p>Add comma to number by every 3 numbers from right. ahead by symbol char. <p>Add comma to a number value by every 3 numbers from right to left. ahead by symbol char. if value is a invalid number string like "aa", return empty string.</p>
Param should be number or numberic string.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func Comma(v any, symbol string) string func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string
``` ```
<b>Example:</b> <b>Example:</b>

View File

@@ -28,12 +28,12 @@ import (
### <span id="Comma">Comma</span> ### <span id="Comma">Comma</span>
<p>用逗号每隔3位分割数字/字符串,签名添加符号。参数必须是数字或者可以转为数字的字符串</p> <p>用逗号每隔3位分割数字/字符串,支持前缀添加符号。参数value必须是数字或者可以转为数字的字符串, 否则返回空字符串</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func Comma(v any, symbol string) string func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string
``` ```
<b>例子:</b> <b>例子:</b>

View File

@@ -22,10 +22,11 @@ import (
## Index ## Index
- [After](#After) - [After](#After)
- [Before](#Before) - [Before](#Before)
- [Curry](#Curry) - [CurryFn](#CurryFn)
- [Compose](#Compose) - [Compose](#Compose)
- [Debounced](#Debounced) - [Debounced](#Debounced)
- [Delay](#Delay) - [Delay](#Delay)
- [Pipeline](#Pipeline)
- [Watcher](#Watcher) - [Watcher](#Watcher)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -124,15 +125,15 @@ func main() {
### <span id="Curry">Curry</span> ### <span id="CurryFn">CurryFn</span>
<p>Make a curry function.</p> <p>Make curry function.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
type Fn func(...any) any type CurryFn[T any] func(...T) T
func (f Fn) Curry(i any) func(...any) any func (cf CurryFn[T]) New(val T) func(...T) T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -148,12 +149,15 @@ func main() {
add := func(a, b int) int { add := func(a, b int) int {
return a + b return a + b
} }
var addCurry function.Fn = func(values ...any) any {
return add(values[0].(int), values[1].(int)) var addCurry CurryFn[int] = func(values ...int) int {
return add(values[0], values[1])
} }
add1 := addCurry.Curry(1) add1 := addCurry.New(1)
result := add1(2) result := add1(2)
fmt.Println(result) //3
fmt.Println(result) //3
} }
``` ```
@@ -166,7 +170,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func Compose(fnList ...func(...any) any) func(...any) any func Compose[T any](fnList ...func(...T) T) func(...T) T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -179,17 +183,17 @@ import (
) )
func main() { func main() {
add1 := func(v ...any) any { toUpper := func(strs ...string) string {
return v[0].(int) + 1 return strings.ToUpper(strs[0])
} }
add2 := func(v ...any) any { toLower := func(strs ...string) string {
return v[0].(int) + 2 return strings.ToLower(strs[0])
} }
transform := Compose(toUpper, toLower)
add3 := function.Compose(add1, add2) result := transform("aBCde")
result := add3(1)
fmt.Println(result) //4 fmt.Println(result) //ABCDE
} }
``` ```
@@ -258,9 +262,9 @@ import (
func main() { func main() {
var print = func(s string) { var print = func(s string) {
fmt.Println(count) //test delay fmt.Println(count) //delay 2 seconds
} }
function.Delay(2*time.Second, print, "test delay") function.Delay(2*time.Second, print, "delay 2 seconds")
} }
``` ```
@@ -300,6 +304,43 @@ func main() {
``` ```
### <span id="Pipeline">Pipeline</span>
<p>Pipeline takes a list of functions and returns a function whose param will be passed into
the functions one by one.</p>
<b>Signature:</b>
```go
func Pipeline[T any](funcs ...func(T) T) func(T) T
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
addOne := func(x int) int {
return x + 1
}
double := func(x int) int {
return 2 * x
}
square := func(x int) int {
return x * x
}
fn := Pipeline(addOne, double, square)
fmt.Println(fn(2)) //36
}
```
### <span id="Watcher">Watcher</span> ### <span id="Watcher">Watcher</span>
@@ -313,6 +354,7 @@ type Watcher struct {
stopTime int64 stopTime int64
excuting bool excuting bool
} }
func NewWatcher() *Watcher
func (w *Watcher) Start() //start the watcher func (w *Watcher) Start() //start the watcher
func (w *Watcher) Stop() //stop the watcher func (w *Watcher) Stop() //stop the watcher
func (w *Watcher) Reset() //reset the watcher func (w *Watcher) Reset() //reset the watcher
@@ -329,7 +371,8 @@ import (
) )
func main() { func main() {
w := &function.Watcher{} w := function.NewWatcher()
w.Start() w.Start()
longRunningTask() longRunningTask()
@@ -339,14 +382,10 @@ func main() {
w.Stop() w.Stop()
eapsedTime := w.GetElapsedTime().Milliseconds() eapsedTime := w.GetElapsedTime().Milliseconds()
fmt.Println(eapsedTime) fmt.Println(eapsedTime)
w.Reset() w.Reset()
fmt.Println(w.excuting) //false
fmt.Println(w.startTime) //0
fmt.Println(w.stopTime) //0
} }
func longRunningTask() { func longRunningTask() {

View File

@@ -22,10 +22,11 @@ import (
## 目录 ## 目录
- [After](#After) - [After](#After)
- [Before](#Before) - [Before](#Before)
- [Curry](#Curry) - [CurryFn](#CurryFn)
- [Compose](#Compose) - [Compose](#Compose)
- [Debounced](#Debounced) - [Debounced](#Debounced)
- [Delay](#Delay) - [Delay](#Delay)
- [Pipeline](#Pipeline)
- [Watcher](#Watcher) - [Watcher](#Watcher)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -123,15 +124,15 @@ func main() {
### <span id="Curry">Curry</span> ### <span id="CurryFn">CurryFn</span>
<p>创建一个柯里化函数</p> <p>创建柯里化函数</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
type Fn func(...any) any type CurryFn[T any] func(...T) T
func (f Fn) Curry(i any) func(...any) any func (cf CurryFn[T]) New(val T) func(...T) T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -147,11 +148,14 @@ func main() {
add := func(a, b int) int { add := func(a, b int) int {
return a + b return a + b
} }
var addCurry function.Fn = func(values ...any) any {
return add(values[0].(int), values[1].(int)) var addCurry CurryFn[int] = func(values ...int) int {
return add(values[0], values[1])
} }
add1 := addCurry.Curry(1) add1 := addCurry.New(1)
result := add1(2) result := add1(2)
fmt.Println(result) //3 fmt.Println(result) //3
} }
``` ```
@@ -160,12 +164,12 @@ func main() {
### <span id="Compose">Compose</span> ### <span id="Compose">Compose</span>
<p>从右至左组合函数列表fnList 返回组合后的函数</p> <p>从右至左组合函数列表fnList返回组合后的函数</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func Compose(fnList ...func(...any) any) func(...any) any func Compose[T any](fnList ...func(...T) T) func(...T) T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -178,17 +182,17 @@ import (
) )
func main() { func main() {
add1 := func(v ...any) any { toUpper := func(strs ...string) string {
return v[0].(int) + 1 return strings.ToUpper(strs[0])
} }
add2 := func(v ...any) any { toLower := func(strs ...string) string {
return v[0].(int) + 2 return strings.ToLower(strs[0])
} }
transform := Compose(toUpper, toLower)
add3 := function.Compose(add1, add2) result := transform("aBCde")
result := add3(1)
fmt.Println(result) //4 fmt.Println(result) //ABCDE
} }
``` ```
@@ -196,7 +200,7 @@ func main() {
### <span id="Debounced">Debounced</span> ### <span id="Debounced">Debounced</span>
<p>创建一个 debounced 函数,该函数延迟调用 fn 直到自上次调用 debounced 函数后等待持续时间过去。</p> <p>创建一个debounced函数该函数延迟调用fn直到自上次调用debounced函数后等待持续时间过去。</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -300,9 +304,47 @@ func main() {
### <span id="Pipeline">Pipeline</span>
<p>执行函数pipeline.</p>
<b>函数签名:</b>
```go
func Pipeline[T any](funcs ...func(T) T) func(T) T
```
<b>例子:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
addOne := func(x int) int {
return x + 1
}
double := func(x int) int {
return 2 * x
}
square := func(x int) int {
return x * x
}
f := Pipeline(addOne, double, square)
fmt.Println(fn(2)) //36
}
```
### <span id="Watcher">Watcher</span> ### <span id="Watcher">Watcher</span>
<p>Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。 </p> <p>Watcher用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -312,6 +354,7 @@ type Watcher struct {
stopTime int64 stopTime int64
excuting bool excuting bool
} }
func NewWatcher() *Watcher
func (w *Watcher) Start() //start the watcher func (w *Watcher) Start() //start the watcher
func (w *Watcher) Stop() //stop the watcher func (w *Watcher) Stop() //stop the watcher
func (w *Watcher) Reset() //reset the watcher func (w *Watcher) Reset() //reset the watcher
@@ -328,7 +371,8 @@ import (
) )
func main() { func main() {
w := &function.Watcher{} w := function.NewWatcher()
w.Start() w.Start()
longRunningTask() longRunningTask()
@@ -338,14 +382,11 @@ func main() {
w.Stop() w.Stop()
eapsedTime := w.GetElapsedTime().Milliseconds() eapsedTime := w.GetElapsedTime().Milliseconds()
fmt.Println(eapsedTime) fmt.Println(eapsedTime)
w.Reset() w.Reset()
fmt.Println(w.excuting) //false
fmt.Println(w.startTime) //0
fmt.Println(w.stopTime) //0
} }
func longRunningTask() { func longRunningTask() {

View File

@@ -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)
@@ -46,7 +45,7 @@ import (
<b>Signature:</b> <b>Signature:</b>
```go ```go
func Average[T lancetconstraints.Number](numbers ...T) T func Average[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -158,7 +157,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func Max[T lancetconstraints.Number](numbers ...T) T func Max[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -219,12 +218,12 @@ func main() {
### <span id="Min">Min</span> ### <span id="Min">Min</span>
<p>Return min value of numbers.</p> <p>Return the minimum value of numbers.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func Min[T lancetconstraints.Number](numbers ...T) T func Min[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -303,8 +302,8 @@ import (
) )
func main() { func main() {
fmt.Println(mathutil.Percent(1, 2, 2)) //1 fmt.Println(mathutil.Percent(1, 2, 2)) //0.5
fmt.Println(mathutil.Percent(0.1, 0.3, 2)) //33.33 fmt.Println(mathutil.Percent(0.1, 0.3, 2)) //0.33
} }
``` ```

View File

@@ -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)
@@ -45,7 +44,7 @@ import (
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func Average[T lancetconstraints.Number](numbers ...T) T func Average[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -155,7 +154,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func Max[T lancetconstraints.Number](numbers ...T) T func Max[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -221,7 +220,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func Min[T lancetconstraints.Number](numbers ...T) T func Min[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -300,8 +299,8 @@ import (
) )
func main() { func main() {
fmt.Println(mathutil.Percent(1, 2, 2)) //1 fmt.Println(mathutil.Percent(1, 2, 2)) //0.5
fmt.Println(mathutil.Percent(0.1, 0.3, 2)) //33.33 fmt.Println(mathutil.Percent(0.1, 0.3, 2)) //0.33
} }
``` ```

View File

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

View File

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

View File

@@ -1,16 +1,17 @@
# Random # Random
Package random implements some basic functions to generate random int and string. Package random implements some basic functions to generate random int and string.
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
## Source: ## Source:
- [https://github.com/duke-git/lancet/blob/main/random/random.go](https://github.com/duke-git/lancet/blob/main/random/random.go) - [https://github.com/duke-git/lancet/blob/main/random/random.go](https://github.com/duke-git/lancet/blob/main/random/random.go)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
## Usage: ## Usage:
```go ```go
import ( import (
"github.com/duke-git/lancet/v2/random" "github.com/duke-git/lancet/v2/random"
@@ -20,17 +21,22 @@ import (
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
## Index ## Index
- [RandBytes](#RandBytes)
- [RandInt](#RandInt) - [RandBytes](#RandBytes)
- [RandString](#RandString) - [RandInt](#RandInt)
- [UUIdV4](#UUIdV4) - [RandString](#RandString)
- [RandUpper](#RandUpper)
- [RandLower](#RandLower)
- [RandNumeral](#RandNumeral)
- [RandNumeralOrLetter](#RandNumeralOrLetter)
- [UUIdV4](#UUIdV4)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
## Documentation ## Documentation
### <span id="RandBytes">RandBytes</span> ### <span id="RandBytes">RandBytes</span>
<p>Generate random byte slice.</p> <p>Generate random byte slice.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -38,6 +44,7 @@ import (
```go ```go
func RandBytes(length int) []byte func RandBytes(length int) []byte
``` ```
<b>Example:</b> <b>Example:</b>
```go ```go
@@ -54,8 +61,8 @@ func main() {
} }
``` ```
### <span id="RandInt">RandInt</span> ### <span id="RandInt">RandInt</span>
<p>Generate random int between min and max, may contain min, not max.</p> <p>Generate random int between min and max, may contain min, not max.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -63,6 +70,7 @@ func main() {
```go ```go
func RandInt(min, max int) int func RandInt(min, max int) int
``` ```
<b>Example:</b> <b>Example:</b>
```go ```go
@@ -79,16 +87,16 @@ func main() {
} }
``` ```
### <span id="RandString">RandString</span>
<p>Generate random given length string. only contains letter (a-zA-Z)</p>
### <span id="RandString">RandInt</span>
<p>Generate random given length string.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func RandString(length int) string func RandString(length int) string
``` ```
<b>Example:</b> <b>Example:</b>
```go ```go
@@ -101,14 +109,116 @@ import (
func main() { func main() {
randStr := random.RandString(6) randStr := random.RandString(6)
fmt.Println(randStr) fmt.Println(randStr) //pGWsze
} }
``` ```
### <span id="RandUpper">RandUpper</span>
<p>Generate a random upper case string</p>
<b>Signature:</b>
```go
func RandUpper(length int) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randStr := random.RandString(6)
fmt.Println(randStr) //PACWGF
}
```
### <span id="RandLower">RandLower</span>
<p>Generate a random lower case string</p>
<b>Signature:</b>
```go
func RandLower(length int) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randStr := random.RandLower(6)
fmt.Println(randStr) //siqbew
}
```
### <span id="RandNumeral">RandNumeral</span>
<p>Generate a random numeral string</p>
<b>Signature:</b>
```go
func RandNumeral(length int) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randStr := random.RandNumeral(6)
fmt.Println(randStr) //035172
}
```
### <span id="RandNumeralOrLetter">RandNumeralOrLetter</span>
<p>generate a random numeral or letter string</p>
<b>Signature:</b>
```go
func RandNumeralOrLetter(length int) string
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randStr := random.RandNumeralOrLetter(6)
fmt.Println(randStr) //0aW7cQ
}
```
### <span id="UUIdV4">UUIdV4</span> ### <span id="UUIdV4">UUIdV4</span>
<p>Generate a random UUID of version 4 according to RFC 4122.</p> <p>Generate a random UUID of version 4 according to RFC 4122.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -116,6 +226,7 @@ func main() {
```go ```go
func UUIdV4() (string, error) func UUIdV4() (string, error)
``` ```
<b>Example:</b> <b>Example:</b>
```go ```go
@@ -134,5 +245,3 @@ func main() {
fmt.Println(uuid) fmt.Println(uuid)
} }
``` ```

View File

@@ -1,16 +1,17 @@
# Random # Random
random随机数生成器包可以生成随机[]bytes, int, string。
random 随机数生成器包,可以生成随机[]bytes, int, string。
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
## 源码: ## 源码:
- [https://github.com/duke-git/lancet/blob/main/random/random.go](https://github.com/duke-git/lancet/blob/main/random/random.go) - [https://github.com/duke-git/lancet/blob/main/random/random.go](https://github.com/duke-git/lancet/blob/main/random/random.go)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
## 用法: ## 用法:
```go ```go
import ( import (
"github.com/duke-git/lancet/v2/random" "github.com/duke-git/lancet/v2/random"
@@ -20,18 +21,22 @@ import (
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
## 目录 ## 目录
- [RandBytes](#RandBytes)
- [RandInt](#RandInt)
- [RandString](#RandString)
- [UUIdV4](#UUIdV4)
- [RandBytes](#RandBytes)
- [RandInt](#RandInt)
- [RandString](#RandString)
- [RandUpper](#RandUpper)
- [RandLower](#RandLower)
- [RandNumeral](#RandNumeral)
- [RandNumeralOrLetter](#RandNumeralOrLetter)
- [UUIdV4](#UUIdV4)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
## 文档 ## 文档
### <span id="RandBytes">RandBytes</span> ### <span id="RandBytes">RandBytes</span>
<p>生成随机字节切片</p> <p>生成随机字节切片</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -39,6 +44,7 @@ import (
```go ```go
func RandBytes(length int) []byte func RandBytes(length int) []byte
``` ```
<b>例子:</b> <b>例子:</b>
```go ```go
@@ -55,8 +61,8 @@ func main() {
} }
``` ```
### <span id="RandInt">RandInt</span> ### <span id="RandInt">RandInt</span>
<p>生成随机int, 范围[min, max)</p> <p>生成随机int, 范围[min, max)</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -64,6 +70,7 @@ func main() {
```go ```go
func RandInt(min, max int) int func RandInt(min, max int) int
``` ```
<b>例子:</b> <b>例子:</b>
```go ```go
@@ -80,16 +87,16 @@ func main() {
} }
``` ```
### <span id="RandString">RandString</span>
<p>生成给定长度的随机字符串,只包含字母(a-zA-Z)</p>
### <span id="RandString">RandInt</span>
<p>生成随机给定长度的随机字符串</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func RandString(length int) string func RandString(length int) string
``` ```
<b>例子:</b> <b>例子:</b>
```go ```go
@@ -102,13 +109,116 @@ import (
func main() { func main() {
randStr := random.RandString(6) randStr := random.RandString(6)
fmt.Println(randStr) fmt.Println(randStr) //pGWsze
} }
``` ```
### <span id="RandUpper">RandUpper</span>
<p>生成给定长度的随机大写字母字符串</p>
<b>函数签名:</b>
```go
func RandUpper(length int) string
```
<b>例子:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randStr := random.RandString(6)
fmt.Println(randStr) //PACWGF
}
```
### <span id="RandLower">RandLower</span>
<p>生成给定长度的随机小写字母字符串</p>
<b>函数签名:</b>
```go
func RandLower(length int) string
```
<b>例子:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randStr := random.RandLower(6)
fmt.Println(randStr) //siqbew
}
```
### <span id="RandNumeral">RandNumeral</span>
<p>生成给定长度的随机数字字符串</p>
<b>函数签名:</b>
```go
func RandNumeral(length int) string
```
<b>例子:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randStr := random.RandNumeral(6)
fmt.Println(randStr) //035172
}
```
### <span id="RandNumeralOrLetter">RandNumeralOrLetter</span>
<p>生成给定长度的随机字符串(数字+字母)</p>
<b>函数签名:</b>
```go
func RandNumeralOrLetter(length int) string
```
<b>例子:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randStr := random.RandNumeralOrLetter(6)
fmt.Println(randStr) //0aW7cQ
}
```
### <span id="UUIdV4">UUIdV4</span> ### <span id="UUIdV4">UUIdV4</span>
<p>生成UUID v4字符串</p> <p>生成UUID v4字符串</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -116,6 +226,7 @@ func main() {
```go ```go
func UUIdV4() (string, error) func UUIdV4() (string, error)
``` ```
<b>例子:</b> <b>例子:</b>
```go ```go
@@ -134,5 +245,3 @@ func main() {
fmt.Println(uuid) fmt.Println(uuid)
} }
``` ```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -28,15 +28,17 @@ import (
- [Capitalize](#Capitalize) - [Capitalize](#Capitalize)
- [IsString](#IsString) - [IsString](#IsString)
- [KebabCase](#KebabCase) - [KebabCase](#KebabCase)
- [UpperKebabCase](#UpperKebabCase)
- [LowerFirst](#LowerFirst) - [LowerFirst](#LowerFirst)
- [UpperFirst](#UpperFirst) - [UpperFirst](#UpperFirst)
- [PadEnd](#PadEnd) - [PadEnd](#PadEnd)
- [PadStart](#PadStart) - [PadStart](#PadStart)
- [Reverse](#Reverse) - [Reverse](#Reverse)
- [SnakeCase](#SnakeCase) - [SnakeCase](#SnakeCase)
- [UpperSnakeCase](#UpperSnakeCase)
- [SplitEx](#SplitEx) - [SplitEx](#SplitEx)
- [Substring](#Substring)
- [Wrap](#Wrap) - [Wrap](#Wrap)
- [Unwrap](#Unwrap) - [Unwrap](#Unwrap)
@@ -46,7 +48,7 @@ import (
### <span id="After">After</span> ### <span id="After">After</span>
<p>Creates substring in source string after position when char first appear.</p> <p>Returns the substring after the first occurrence of a specified string in the source string.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -76,7 +78,7 @@ func main() {
### <span id="AfterLast">AfterLast</span> ### <span id="AfterLast">AfterLast</span>
<p>Creates substring in source string after position when char last appear.</p> <p>Returns the substring after the last occurrence of a specified string in the source string.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -107,7 +109,7 @@ func main() {
### <span id="Before">Before</span> ### <span id="Before">Before</span>
<p>Creates substring in source string before position when char first appear.</p> <p>Returns the substring of the source string up to the first occurrence of the specified string.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -138,7 +140,7 @@ func main() {
### <span id="BeforeLast">BeforeLast</span> ### <span id="BeforeLast">BeforeLast</span>
<p>Creates substring in source string before position when char first appear.</p> <p>Returns the substring of the source string up to the last occurrence of the specified string.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -166,10 +168,8 @@ func main() {
``` ```
### <span id="CamelCase">CamelCase</span> ### <span id="CamelCase">CamelCase</span>
<p>Covert string to camelCase string.</p> <p>Coverts string to camelCase string, non letters and numbers will be ignored.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -196,6 +196,75 @@ func main() {
s4 := strutil.CamelCase("foo bar") s4 := strutil.CamelCase("foo bar")
fmt.Println(s4) //fooBar fmt.Println(s4) //fooBar
s4 := strutil.CamelCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s4) //foo11Bar
}
```
### <span id="KebabCase">KebabCase</span>
<p>KebabCase covert string to kebab-case, non letters and numbers will be ignored.</p>
<b>Signature:</b>
```go
func KebabCase(s string) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
s1 := strutil.KebabCase("Foo Bar-")
fmt.Println(s1) //foo-bar
s2 := strutil.KebabCase("foo_Bar")
fmt.Println(s2) //foo-bar
s3 := strutil.KebabCase("fooBar")
fmt.Println(s3) //foo-bar
s4 := strutil.KebabCase("__FOO_BAR__")
fmt.Println(s4) //foo-bar
}
```
### <span id="UpperKebabCase">UpperKebabCase</span>
<p>UpperKebabCase covert string to upper KEBAB-CASE, non letters and numbers will be ignored.</p>
<b>Signature:</b>
```go
func KebabCase(s string) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
s1 := strutil.UpperKebabCase("Foo Bar-")
fmt.Println(s1) //FOO-BAR
s2 := strutil.UpperKebabCase("foo_Bar")
fmt.Println(s2) //FOO-BAR
s3 := strutil.UpperKebabCase("fooBar")
fmt.Println(s3) //FOO-BAR
s4 := strutil.UpperKebabCase("__FOO_BAR__")
fmt.Println(s4) //FOO-BAR
} }
``` ```
@@ -458,7 +527,7 @@ func main() {
### <span id="SnakeCase">SnakeCase</span> ### <span id="SnakeCase">SnakeCase</span>
<p>Covert string to snake_case.</p> <p>Coverts string to snake_case, non letters and numbers will be ignored.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -484,14 +553,47 @@ func main() {
fmt.Println(s3) //foo_bar fmt.Println(s3) //foo_bar
s4 := strutil.SnakeCase("__FOO_BAR__") s4 := strutil.SnakeCase("__FOO_BAR__")
fmt.Println(s4) //f_o_o_b_a_r fmt.Println(s4) //foo_bar
s5 := strutil.SnakeCase("aBbc-s$@a&%_B.B^C") s5 := strutil.SnakeCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s5) //a_bbc_s_a_b_b_c fmt.Println(s5) //foo_1_1_bar
} }
``` ```
### <span id="UpperSnakeCase">UpperSnakeCase</span>
<p>Coverts string to upper KEBAB-CASE, non letters and numbers will be ignored.</p>
<b>Signature:</b>
```go
func SnakeCase(s string) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
s1 := strutil.UpperSnakeCase("Foo Bar-")
fmt.Println(s1) //FOO_BAR
s2 := strutil.UpperSnakeCase("foo_Bar")
fmt.Println(s2) //FOO_BAR
s3 := strutil.UpperSnakeCase("fooBar")
fmt.Println(s3) //FOO_BAR
s4 := strutil.UpperSnakeCase("__FOO_BAR__")
fmt.Println(s4) //FOO_BAR
s5 := strutil.UpperSnakeCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s5) //FOO_1_1_BAR
}
```
### <span id="SplitEx">SplitEx</span> ### <span id="SplitEx">SplitEx</span>
@@ -530,9 +632,45 @@ func main() {
### <span id="Substring">Substring</span>
<p>Returns a substring of the specified length starting at the specified offset position.</p>
<b>Signature:</b>
```go
func Substring(s string, offset int, length uint) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Substring("abcde", 1, 3)
fmt.Println(result1) //bcd
result2 := strutil.Substring("abcde", 1, 5)
fmt.Println(result2) //bcde
result3 := strutil.Substring("abcde", -1, 3)
fmt.Println(result3) //e
result4 := strutil.Substring("abcde", -2, 2)
fmt.Println(result4) //de
result5 := strutil.Substring("abcde", -2, 3)
fmt.Println(result5) //de
result6 := strutil.Substring("你好,欢迎你", 0, 2)
fmt.Println(result6) //你好
}
```
### <span id="Wrap">Wrap</span> ### <span id="Wrap">Wrap</span>
<p>Wrap a string with another string.</p> <p>Wrap a string with given string.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -569,7 +707,7 @@ func main() {
### <span id="Wrap">Wrap</span> ### <span id="Wrap">Wrap</span>
<p>Unwrap a given string from anther string. will change str value.</p> <p>Unwrap a given string from anther string. will change source string.</p>
<b>Signature:</b> <b>Signature:</b>

View File

@@ -28,15 +28,17 @@ import (
- [Capitalize](#Capitalize) - [Capitalize](#Capitalize)
- [IsString](#IsString) - [IsString](#IsString)
- [KebabCase](#KebabCase) - [KebabCase](#KebabCase)
- [UpperKebabCase](#UpperKebabCase)
- [LowerFirst](#LowerFirst) - [LowerFirst](#LowerFirst)
- [UpperFirst](#UpperFirst) - [UpperFirst](#UpperFirst)
- [PadEnd](#PadEnd) - [PadEnd](#PadEnd)
- [PadStart](#PadStart) - [PadStart](#PadStart)
- [Reverse](#Reverse) - [Reverse](#Reverse)
- [SnakeCase](#SnakeCase) - [SnakeCase](#SnakeCase)
- [UpperSnakeCase](#UpperSnakeCase)
- [SplitEx](#SplitEx) - [SplitEx](#SplitEx)
- [Substring](#Substring)
- [Wrap](#Wrap) - [Wrap](#Wrap)
- [Unwrap](#Unwrap) - [Unwrap](#Unwrap)
@@ -47,7 +49,7 @@ import (
### <span id="After">After</span> ### <span id="After">After</span>
<p>截取源字符串中char首次出现时的位置之后的子字符串</p> <p>返回源字符串中特定字符串首次出现时的位置之后的子字符串</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -77,7 +79,7 @@ func main() {
### <span id="AfterLast">AfterLast</span> ### <span id="AfterLast">AfterLast</span>
<p>截取源字符串中char最后一次出现时的位置之后的子字符串</p> <p>返回源字符串中指定字符串最后一次出现时的位置之后的子字符串</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -108,7 +110,7 @@ func main() {
### <span id="Before">Before</span> ### <span id="Before">Before</span>
<p>截取源字符串中char首次出现时的位置之前的子字符串</p> <p>返回源字符串中指定字符串第一次出现时的位置之前的子字符串</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -139,7 +141,7 @@ func main() {
### <span id="BeforeLast">BeforeLast</span> ### <span id="BeforeLast">BeforeLast</span>
<p>截取源字符串中char最后一次出现时的位置之前的子字符串</p> <p>返回源字符串中指定字符串最后一次出现时的位置之前的子字符串</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -170,7 +172,7 @@ func main() {
### <span id="CamelCase">CamelCase</span> ### <span id="CamelCase">CamelCase</span>
<p>将字符串转换为驼峰式字符串</p> <p>将字符串转换为驼峰式字符串, 非字母和数字会被忽略</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -197,6 +199,9 @@ func main() {
s4 := strutil.CamelCase("foo bar") s4 := strutil.CamelCase("foo bar")
fmt.Println(s4) //fooBar fmt.Println(s4) //fooBar
s4 := strutil.CamelCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s4) //foo11Bar
} }
``` ```
@@ -234,7 +239,7 @@ func main() {
### <span id="IsString">IsString</span> ### <span id="IsString">IsString</span>
<p>检查值的数据类型是否为字符串</p> <p>判断传入参数的数据类型是否为字符串</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -262,7 +267,7 @@ func main() {
### <span id="KebabCase">KebabCase</span> ### <span id="KebabCase">KebabCase</span>
<p>将字符串转换为kebab-case</p> <p>将字符串转换为kebab-case, 非字母和数字会被忽略</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -288,7 +293,40 @@ func main() {
fmt.Println(s3) //foo-bar fmt.Println(s3) //foo-bar
s4 := strutil.KebabCase("__FOO_BAR__") s4 := strutil.KebabCase("__FOO_BAR__")
fmt.Println(s4) //f-o-o-b-a-r fmt.Println(s4) //foo-bar
}
```
### <span id="UpperKebabCase">UpperKebabCase</span>
<p>将字符串转换为大写KEBAB-CASE, 非字母和数字会被忽略</p>
<b>函数签名:</b>
```go
func KebabCase(s string) string
```
<b>例子:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
s1 := strutil.UpperKebabCase("Foo Bar-")
fmt.Println(s1) //FOO-BAR
s2 := strutil.UpperKebabCase("foo_Bar")
fmt.Println(s2) //FOO-BAR
s3 := strutil.UpperKebabCase("fooBar")
fmt.Println(s3) //FOO-BAR
s4 := strutil.UpperKebabCase("__FOO_BAR__")
fmt.Println(s4) //FOO-BAR
} }
``` ```
@@ -330,7 +368,7 @@ func main() {
### <span id="UpperFirst">UpperFirst</span> ### <span id="UpperFirst">UpperFirst</span>
<p>将字符串的第一个字符转换为大写</p> <p>将字符串的第一个字符转换为大写形式</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -459,7 +497,7 @@ func main() {
### <span id="SnakeCase">SnakeCase</span> ### <span id="SnakeCase">SnakeCase</span>
<p>将字符串转换为snake_case形式</p> <p>将字符串转换为snake_case形式, 非字母和数字会被忽略</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -485,10 +523,45 @@ func main() {
fmt.Println(s3) //foo_bar fmt.Println(s3) //foo_bar
s4 := strutil.SnakeCase("__FOO_BAR__") s4 := strutil.SnakeCase("__FOO_BAR__")
fmt.Println(s4) //f_o_o_b_a_r fmt.Println(s4) //foo_bar
s5 := strutil.SnakeCase("aBbc-s$@a&%_B.B^C") s5 := strutil.SnakeCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s5) //a_bbc_s_a_b_b_c fmt.Println(s5) //foo_1_1_bar
}
```
### <span id="UpperSnakeCase">UpperSnakeCase</span>
<p>将字符串转换为大写SNAKE_CASE形式, 非字母和数字会被忽略</p>
<b>函数签名:</b>
```go
func SnakeCase(s string) string
```
<b>例子:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
s1 := strutil.UpperSnakeCase("Foo Bar-")
fmt.Println(s1) //FOO_BAR
s2 := strutil.UpperSnakeCase("foo_Bar")
fmt.Println(s2) //FOO_BAR
s3 := strutil.UpperSnakeCase("fooBar")
fmt.Println(s3) //FOO_BAR
s4 := strutil.UpperSnakeCase("__FOO_BAR__")
fmt.Println(s4) //FOO_BAR
s5 := strutil.UpperSnakeCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s5) //FOO_1_1_BAR
} }
``` ```
@@ -528,6 +601,42 @@ func main() {
} }
``` ```
### <span id="Substring">Substring</span>
<p>根据指定的位置和长度截取子字符串</p>
<b>函数签名:</b>
```go
func Substring(s string, offset int, length uint) string
```
<b>例子:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Substring("abcde", 1, 3)
fmt.Println(result1) //bcd
result2 := strutil.Substring("abcde", 1, 5)
fmt.Println(result2) //bcde
result3 := strutil.Substring("abcde", -1, 3)
fmt.Println(result3) //e
result4 := strutil.Substring("abcde", -2, 2)
fmt.Println(result4) //de
result5 := strutil.Substring("abcde", -2, 3)
fmt.Println(result5) //de
result6 := strutil.Substring("你好,欢迎你", 0, 2)
fmt.Println(result6) //你好
}
```
### <span id="Wrap">Wrap</span> ### <span id="Wrap">Wrap</span>

View File

@@ -102,7 +102,7 @@ import (
) )
func main() { func main() {
isOsMac := system.IsMac isOsMac := system.IsMac()
fmt.Println(isOsMac) fmt.Println(isOsMac)
} }
``` ```
@@ -211,7 +211,7 @@ func main() {
### <span id="ExecCommand">CompareOsEnv</span> ### <span id="ExecCommand">CompareOsEnv</span>
<p>Use shell /bin/bash -c(linux) or cmd (windows) to execute command.</p> <p>Execute shell command, return the stdout and stderr string of command, and error if error occur. param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1. In linux, use /bin/bash -c to execute command, In windows, use powershell.exe to execute command.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -227,10 +227,24 @@ import (
) )
func main() { func main() {
out, errout, err := system.ExecCommand("ls") // linux or mac
fmt.Println(out) stdout, stderr, err := system.ExecCommand("ls")
fmt.Println(errout) fmt.Println("std out: ", stdout)
fmt.Println(err) fmt.Println("std err: ", stderr)
assert.Equal("", stderr)
// windows
stdout, stderr, err = system.ExecCommand("dir")
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
// error command
stdout, stderr, err = system.ExecCommand("abc")
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
if err != nil {
fmt.Println(err.Error())
}
} }
``` ```

View File

@@ -26,7 +26,6 @@ import (
- [GetOsEnv](#GetOsEnv) - [GetOsEnv](#GetOsEnv)
- [SetOsEnv](#SetOsEnv) - [SetOsEnv](#SetOsEnv)
- [RemoveOsEnv](#RemoveOsEnv) - [RemoveOsEnv](#RemoveOsEnv)
- [CompareOsEnv](#CompareOsEnv) - [CompareOsEnv](#CompareOsEnv)
- [ExecCommand](#ExecCommand) - [ExecCommand](#ExecCommand)
- [GetOsBits](#GetOsBits) - [GetOsBits](#GetOsBits)
@@ -103,7 +102,7 @@ import (
) )
func main() { func main() {
isOsMac := system.IsMac isOsMac := system.IsMac()
fmt.Println(isOsMac) fmt.Println(isOsMac)
} }
``` ```
@@ -212,7 +211,7 @@ func main() {
### <span id="ExecCommand">ExecCommand</span> ### <span id="ExecCommand">ExecCommand</span>
<p>使用shell /bin/bash -c(linux) 或 cmd (windows) 执行shell命令</p> <p>执行shell命令返回命令的stdout和stderr字符串如果出现错误则返回错误。参数`command`是一个完整的命令字符串如ls-alinuxdirwindowsping 127.0.0.1。在linux中使用/bin/bash-c执行命令在windows中使用powershell.exe执行命令</p>
<b>Signature:</b> <b>Signature:</b>
@@ -228,10 +227,24 @@ import (
) )
func main() { func main() {
out, errout, err := system.ExecCommand("ls") // linux or mac
fmt.Println(out) stdout, stderr, err := system.ExecCommand("ls")
fmt.Println(errout) fmt.Println("std out: ", stdout)
fmt.Println(err) fmt.Println("std err: ", stderr)
assert.Equal("", stderr)
// windows
stdout, stderr, err = system.ExecCommand("dir")
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
// error command
stdout, stderr, err = system.ExecCommand("abc")
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
if err != nil {
fmt.Println(err.Error())
}
} }
``` ```

View File

@@ -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)
@@ -48,6 +47,7 @@ import (
- [IsUrl](#IsUrl) - [IsUrl](#IsUrl)
- [IsWeakPassword](#IsWeakPassword) - [IsWeakPassword](#IsWeakPassword)
- [IsZeroValue](#IsZeroValue) - [IsZeroValue](#IsZeroValue)
- [IsGBK](#IsGBK)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -822,7 +822,34 @@ func main() {
``` ```
### <span id="IsGBK">IsGBK</span>
<p>Checks if data encoding is gbk(Chinese character internal code extension specification). this function is implemented by whether double bytes fall within the encoding range of gbk,while each byte of utf-8 encoding format falls within the encoding range of gbk.Therefore, utf8.valid() should be called first to check whether it is not utf-8 encoding and then call IsGBK() to check gbk encoding. like the example.</p>
<b>Signature:</b>
```go
func IsGBK(data []byte) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
data := []byte("你好")
// check utf8 first
if utf8.Valid(data) {
fmt.Println("data encoding is utf-8")
}else if(validator.IsGBK(data)) {
fmt.Println("data encoding is GBK")
}
fmt.Println("data encoding is unknown")
}
```

View File

@@ -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)
@@ -48,6 +47,7 @@ import (
- [IsUrl](#IsUrl) - [IsUrl](#IsUrl)
- [IsWeakPassword](#IsWeakPassword) - [IsWeakPassword](#IsWeakPassword)
- [IsZeroValue](#IsZeroValue) - [IsZeroValue](#IsZeroValue)
- [IsGBK](#IsGBK)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -459,8 +459,6 @@ func main() {
### <span id="IsEmptyString">IsEmptyString</span> ### <span id="IsEmptyString">IsEmptyString</span>
<p>验证字符串是否是空字符串</p> <p>验证字符串是否是空字符串</p>
@@ -488,7 +486,6 @@ func main() {
### <span id="IsFloatStr">IsFloatStr</span> ### <span id="IsFloatStr">IsFloatStr</span>
<p>验证字符串是否是可以转换为浮点数</p> <p>验证字符串是否是可以转换为浮点数</p>
@@ -574,8 +571,6 @@ func main() {
``` ```
### <span id="IsRegexMatch">IsRegexMatch</span> ### <span id="IsRegexMatch">IsRegexMatch</span>
<p>验证字符串是否可以匹配正则表达式</p> <p>验证字符串是否可以匹配正则表达式</p>
@@ -601,7 +596,6 @@ func main() {
### <span id="IsIntStr">IsIntStr</span> ### <span id="IsIntStr">IsIntStr</span>
<p>验证字符串是否是可以转换为整数</p> <p>验证字符串是否是可以转换为整数</p>
@@ -821,3 +815,31 @@ func main() {
} }
``` ```
### <span id="IsGBK">IsGBK</span>
<p>检查数据编码是否为gbk汉字内部代码扩展规范。该函数的实现取决于双字节是否在gbk的编码范围内而utf-8编码格式的每个字节都在gbk编码范围内。因此应该首先调用utf8.valid检查它是否是utf-8编码然后调用IsGBK检查gbk编码。如示例所示。</p>
<b>函数签名:</b>
```go
func IsGBK(data []byte) bool
```
<b>例子:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
data := []byte("你好")
// 先检查utf8编码
if utf8.Valid(data) {
fmt.Println("data encoding is utf-8")
}else if(validator.IsGBK(data)) {
fmt.Println("data encoding is GBK")
}
fmt.Println("data encoding is unknown")
}
```

View File

@@ -8,9 +8,9 @@ import (
"archive/zip" "archive/zip"
"bufio" "bufio"
"errors" "errors"
"fmt"
"io" "io"
"io/fs" "io/fs"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path" "path"
@@ -18,7 +18,8 @@ import (
"strings" "strings"
) )
// IsExist checks if a file or directory exists // IsExist checks if a file or directory exists.
// Play: https://go.dev/play/p/nKKXt8ZQbmh
func IsExist(path string) bool { func IsExist(path string) bool {
_, err := os.Stat(path) _, err := os.Stat(path)
if err == nil { if err == nil {
@@ -30,7 +31,8 @@ func IsExist(path string) bool {
return false return false
} }
// CreateFile create a file in path // CreateFile create a file in path.
// Play: https://go.dev/play/p/lDt8PEsTNKI
func CreateFile(path string) bool { func CreateFile(path string) bool {
file, err := os.Create(path) file, err := os.Create(path)
if err != nil { if err != nil {
@@ -41,12 +43,14 @@ func CreateFile(path string) bool {
return true return true
} }
// CreateDir create directory in absolute path. param `absPath` like /a/, /a/b/ // CreateDir create directory in absolute path. param `absPath` like /a/, /a/b/.
// Play: https://go.dev/play/p/qUuCe1OGQnM
func CreateDir(absPath string) error { func CreateDir(absPath string) error {
return os.MkdirAll(path.Dir(absPath), os.ModePerm) return os.MkdirAll(path.Dir(absPath), os.ModePerm)
} }
// IsDir checks if the path is directory or not // IsDir checks if the path is directory or not.
// Play: https://go.dev/play/p/WkVwEKqtOWk
func IsDir(path string) bool { func IsDir(path string) bool {
file, err := os.Stat(path) file, err := os.Stat(path)
if err != nil { if err != nil {
@@ -55,12 +59,14 @@ func IsDir(path string) bool {
return file.IsDir() return file.IsDir()
} }
// RemoveFile remove the path file // RemoveFile remove the path file.
// Play: https://go.dev/play/p/P2y0XW8a1SH
func RemoveFile(path string) error { func RemoveFile(path string) error {
return os.Remove(path) return os.Remove(path)
} }
// CopyFile copy src file to dest file // CopyFile copy src file to dest file.
// Play: https://go.dev/play/p/Jg9AMJMLrJi
func CopyFile(srcFilePath string, dstFilePath string) error { func CopyFile(srcFilePath string, dstFilePath string) error {
srcFile, err := os.Open(srcFilePath) srcFile, err := os.Open(srcFilePath)
if err != nil { if err != nil {
@@ -77,17 +83,21 @@ func CopyFile(srcFilePath string, dstFilePath string) error {
var tmp = make([]byte, 1024*4) var tmp = make([]byte, 1024*4)
for { for {
n, err := srcFile.Read(tmp) n, err := srcFile.Read(tmp)
distFile.Write(tmp[:n])
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
return nil return nil
} }
return err return err
} }
_, err = distFile.Write(tmp[:n])
if err != nil {
return err
}
} }
} }
//ClearFile write empty string to path file // ClearFile write empty string to path file.
// Play: https://go.dev/play/p/NRZ0ZT-G94H
func ClearFile(path string) error { func ClearFile(path string) error {
f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
if err != nil { if err != nil {
@@ -99,16 +109,18 @@ func ClearFile(path string) error {
return err return err
} }
//ReadFileToString return string of file content // ReadFileToString return string of file content.
// Play: https://go.dev/play/p/cmfwp_5SQTp
func ReadFileToString(path string) (string, error) { func ReadFileToString(path string) (string, error) {
bytes, err := ioutil.ReadFile(path) bytes, err := os.ReadFile(path)
if err != nil { if err != nil {
return "", err return "", err
} }
return string(bytes), nil return string(bytes), nil
} }
// ReadFileByLine read file line by line // ReadFileByLine read file line by line.
// Play: https://go.dev/play/p/svJP_7ZrBrD
func ReadFileByLine(path string) ([]string, error) { func ReadFileByLine(path string) ([]string, error) {
f, err := os.Open(path) f, err := os.Open(path)
if err != nil { if err != nil {
@@ -134,13 +146,14 @@ func ReadFileByLine(path string) ([]string, error) {
return result, nil return result, nil
} }
// ListFileNames return all file names in the path // ListFileNames return all file names in the path.
// Play: https://go.dev/play/p/Tjd7Y07rejl
func ListFileNames(path string) ([]string, error) { func ListFileNames(path string) ([]string, error) {
if !IsExist(path) { if !IsExist(path) {
return []string{}, nil return []string{}, nil
} }
fs, err := ioutil.ReadDir(path) fs, err := os.ReadDir(path)
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
@@ -160,7 +173,8 @@ func ListFileNames(path string) ([]string, error) {
return result, nil return result, nil
} }
// Zip create zip file, fpath could be a single file or a directory // Zip create zip file, fpath could be a single file or a directory.
// Play: https://go.dev/play/p/j-3sWBp8ik_P
func Zip(fpath string, destPath string) error { func Zip(fpath string, destPath string) error {
zipFile, err := os.Create(destPath) zipFile, err := os.Create(destPath)
if err != nil { if err != nil {
@@ -171,7 +185,7 @@ func Zip(fpath string, destPath string) error {
archive := zip.NewWriter(zipFile) archive := zip.NewWriter(zipFile)
defer archive.Close() defer archive.Close()
filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error { err = filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
@@ -208,11 +222,17 @@ func Zip(fpath string, destPath string) error {
return nil return nil
}) })
if err != nil {
return err
}
return nil return nil
} }
// UnZip unzip the file and save it to destPath // UnZip unzip the file and save it to destPath.
// Play: https://go.dev/play/p/g0w34kS7B8m
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,11 +240,20 @@ 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) err = os.MkdirAll(path, os.ModePerm)
if err != nil {
return err
}
} else { } else {
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { err = os.MkdirAll(filepath.Dir(path), os.ModePerm)
if err != nil {
return err return err
} }
@@ -249,7 +278,19 @@ func UnZip(zipFile string, destPath string) error {
return nil return nil
} }
// IsLink checks if a file is symbol link or not 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.
// Play: https://go.dev/play/p/TL-b-Kzvf44
func IsLink(path string) bool { func IsLink(path string) bool {
fi, err := os.Lstat(path) fi, err := os.Lstat(path)
if err != nil { if err != nil {
@@ -258,7 +299,8 @@ func IsLink(path string) bool {
return fi.Mode()&os.ModeSymlink != 0 return fi.Mode()&os.ModeSymlink != 0
} }
// FileMode return file's mode and permission // FileMode return file's mode and permission.
// Play: https://go.dev/play/p/2l2hI42fA3p
func FileMode(path string) (fs.FileMode, error) { func FileMode(path string) (fs.FileMode, error) {
fi, err := os.Lstat(path) fi, err := os.Lstat(path)
if err != nil { if err != nil {
@@ -268,7 +310,8 @@ func FileMode(path string) (fs.FileMode, error) {
} }
// MiMeType return file mime type // MiMeType return file mime type
// param `file` should be string(file path) or *os.File // param `file` should be string(file path) or *os.File.
// Play: https://go.dev/play/p/bd5sevSUZNu
func MiMeType(file any) string { func MiMeType(file any) string {
var mediatype string var mediatype string

View File

@@ -0,0 +1,225 @@
package fileutil
import (
"fmt"
"os"
)
func ExampleIsExist() {
result1 := IsExist("./")
result2 := IsExist("./xxx.go")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
func ExampleCreateFile() {
fname := "./test.txt"
result1 := IsExist(fname)
CreateFile(fname)
result2 := IsExist(fname)
os.Remove(fname)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// false
// true
}
func ExampleCreateDir() {
pwd, _ := os.Getwd()
dirPath := pwd + "/test_xxx/"
result1 := IsExist(dirPath)
err := CreateDir(dirPath)
if err != nil {
return
}
result2 := IsExist(dirPath)
os.Remove(dirPath)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// false
// true
}
func ExampleIsDir() {
result1 := IsDir("./")
result2 := IsDir("./xxx.go")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
func ExampleRemoveFile() {
srcFile := "./text.txt"
CreateFile(srcFile)
copyFile := "./text_copy.txt"
err := CopyFile(srcFile, copyFile)
if err != nil {
return
}
file, err := os.Open(copyFile)
if err != nil {
return
}
result1 := IsExist(copyFile)
result2 := file.Name()
os.Remove(srcFile)
os.Remove(copyFile)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// ./text_copy.txt
}
func ExampleReadFileToString() {
fname := "./test.txt"
CreateFile(fname)
f, _ := os.OpenFile(fname, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
_, err := f.WriteString("hello world")
if err != nil {
return
}
content, _ := ReadFileToString(fname)
os.Remove(fname)
fmt.Println(content)
// Output:
// hello world
}
func ExampleClearFile() {
fname := "./test.txt"
CreateFile(fname)
f, _ := os.OpenFile(fname, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
_, err := f.WriteString("hello world")
if err != nil {
return
}
content1, _ := ReadFileToString(fname)
err = ClearFile(fname)
if err != nil {
return
}
content2, _ := ReadFileToString(fname)
os.Remove(fname)
fmt.Println(content1)
fmt.Println(content2)
// Output:
// hello world
//
}
func ExampleReadFileByLine() {
fname := "./test.txt"
CreateFile(fname)
f, _ := os.OpenFile(fname, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
_, err := f.WriteString("hello\nworld")
if err != nil {
return
}
content, _ := ReadFileByLine(fname)
os.Remove(fname)
fmt.Println(content)
// Output:
// [hello world]
}
func ExampleListFileNames() {
fileList, _ := ListFileNames("./")
fmt.Println(fileList)
// Output:
// [file.go file_example_test.go file_test.go]
}
func ExampleZip() {
srcFile := "./test.txt"
CreateFile(srcFile)
zipFile := "./test.zip"
err := Zip(srcFile, zipFile)
if err != nil {
return
}
result := IsExist(zipFile)
os.Remove(srcFile)
os.Remove(zipFile)
fmt.Println(result)
// Output:
// true
}
func ExampleUnZip() {
fname := "./test.txt"
file, _ := os.Create(fname)
_, err := file.WriteString("hello\nworld")
if err != nil {
return
}
f, _ := os.Open(fname)
defer f.Close()
mimeType := MiMeType(f)
fmt.Println(mimeType)
os.Remove(fname)
// Output:
// application/octet-stream
}

View File

@@ -25,9 +25,10 @@ func TestCreateFile(t *testing.T) {
f := "./text.txt" f := "./text.txt"
if CreateFile(f) { if CreateFile(f) {
file, err := os.Open(f) file, err := os.Open(f)
defer file.Close()
assert.IsNil(err) assert.IsNil(err)
assert.Equal(f, file.Name()) assert.Equal(f, file.Name())
defer file.Close()
} else { } else {
t.FailNow() t.FailNow()
} }
@@ -113,7 +114,11 @@ func TestReadFileToString(t *testing.T) {
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close() defer f.Close()
f.WriteString("hello world")
_, err := f.WriteString("hello world")
if err != nil {
t.Log(err)
}
content, _ := ReadFileToString(path) content, _ := ReadFileToString(path)
assert.Equal("hello world", content) assert.Equal("hello world", content)
@@ -130,9 +135,12 @@ func TestClearFile(t *testing.T) {
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close() defer f.Close()
f.WriteString("hello world") _, err := f.WriteString("hello world")
if err != nil {
t.Log(err)
}
err := ClearFile(path) err = ClearFile(path)
assert.IsNil(err) assert.IsNil(err)
content, _ := ReadFileToString(path) content, _ := ReadFileToString(path)
@@ -148,8 +156,13 @@ func TestReadFileByLine(t *testing.T) {
CreateFile(path) CreateFile(path)
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close() defer f.Close()
f.WriteString("hello\nworld")
_, err := f.WriteString("hello\nworld")
if err != nil {
t.Log(err)
}
expected := []string{"hello", "world"} expected := []string{"hello", "world"}
actual, _ := ReadFileByLine(path) actual, _ := ReadFileByLine(path)
@@ -166,10 +179,14 @@ func TestZipAndUnZip(t *testing.T) {
file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, 0777) file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, 0777)
defer file.Close() defer file.Close()
file.WriteString("hello\nworld")
_, err := file.WriteString("hello\nworld")
if err != nil {
t.Fail()
}
zipFile := "./text.zip" zipFile := "./text.zip"
err := Zip(srcFile, zipFile) err = Zip(srcFile, zipFile)
assert.IsNil(err) assert.IsNil(err)
unZipPath := "./unzip" unZipPath := "./unzip"

View File

@@ -4,14 +4,68 @@
// Package formatter implements some functions to format string, struct. // Package formatter implements some functions to format string, struct.
package formatter package formatter
import "strings" import (
"fmt"
"reflect"
"strconv"
"strings"
"golang.org/x/exp/constraints"
)
// Comma add comma to a number value by every 3 numbers from right. ahead by symbol char.
// if value is invalid number string eg "aa", return empty string
// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345"
// Play: https://go.dev/play/p/eRD5k2vzUVX
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string {
s, err := numberToString(value)
if err != nil {
return ""
}
// Comma add comma to number by every 3 numbers from right. ahead by symbol char
func Comma(v any, symbol string) string {
s := numString(v)
dotIndex := strings.Index(s, ".") dotIndex := strings.Index(s, ".")
if dotIndex != -1 { if dotIndex != -1 {
return symbol + commaString(s[:dotIndex]) + s[dotIndex:] return symbol + commaString(s[:dotIndex]) + s[dotIndex:]
} }
return symbol + commaString(s) return symbol + commaString(s)
} }
func commaString(s string) string {
if len(s) <= 3 {
return s
}
return commaString(s[:len(s)-3]) + "," + commaString(s[len(s)-3:])
}
func numberToString(value any) (string, error) {
switch reflect.TypeOf(value).Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return fmt.Sprintf("%v", value), nil
// todo: need to handle 12345678.9 => 1.23456789e+07
case reflect.Float32, reflect.Float64:
return fmt.Sprintf("%v", value), nil
case reflect.String:
{
sv := fmt.Sprintf("%v", value)
if strings.Contains(sv, ".") {
_, err := strconv.ParseFloat(sv, 64)
if err != nil {
return "", err
}
return sv, nil
} else {
_, err := strconv.ParseInt(sv, 10, 64)
if err != nil {
return "", nil
}
return sv, nil
}
}
default:
return "", nil
}
}

View File

@@ -0,0 +1,18 @@
package formatter
import "fmt"
func ExampleComma() {
result1 := Comma("123", "")
result2 := Comma("12345", "$")
result3 := Comma(1234567, "¥")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 123
// $12,345
// ¥1,234,567
}

View File

@@ -1,40 +0,0 @@
package formatter
import (
"fmt"
"reflect"
"strconv"
"strings"
)
func commaString(s string) string {
if len(s) <= 3 {
return s
}
return commaString(s[:len(s)-3]) + "," + commaString(s[len(s)-3:])
}
func numString(value any) string {
switch reflect.TypeOf(value).Kind() {
case reflect.Int, reflect.Int64, reflect.Float32, reflect.Float64:
return fmt.Sprintf("%v", value)
case reflect.String:
{
sv := fmt.Sprintf("%v", value)
if strings.Contains(sv, ".") {
_, err := strconv.ParseFloat(sv, 64)
if err == nil {
return sv
}
} else {
_, err := strconv.ParseInt(sv, 10, 64)
if err == nil {
return sv
}
}
}
default:
return ""
}
return ""
}

View File

@@ -12,12 +12,16 @@ func TestComma(t *testing.T) {
assert.Equal("", Comma("", "")) assert.Equal("", Comma("", ""))
assert.Equal("", Comma("aa", "")) assert.Equal("", Comma("aa", ""))
assert.Equal("", Comma("aa.a", "")) assert.Equal("", Comma("aa.a", ""))
assert.Equal("", Comma([]int{1}, ""))
assert.Equal("123", Comma("123", "")) assert.Equal("123", Comma("123", ""))
assert.Equal("12,345", Comma("12345", "")) assert.Equal("12,345", Comma("12345", ""))
assert.Equal("12,345.6789", Comma("12345.6789", ""))
assert.Equal("123,456,789,000", Comma("123456789000", ""))
assert.Equal("12,345", Comma(12345, "")) assert.Equal("12,345", Comma(12345, ""))
assert.Equal("$12,345", Comma(12345, "$")) assert.Equal("$12,345", Comma(12345, "$"))
assert.Equal("¥12,345", Comma(12345, "¥")) assert.Equal("¥12,345", Comma(12345, "¥"))
assert.Equal("12,345.6789", Comma(12345.6789, "")) assert.Equal("12,345.6789", Comma(12345.6789, ""))
assert.Equal("12,345.6789", Comma(+12345.6789, ""))
// assert.Equal("12,345,678.9", Comma(12345678.9, ""))
assert.Equal("123,456,789,000", Comma(123456789000, ""))
} }

View File

@@ -5,11 +5,13 @@
package function package function
import ( import (
"fmt"
"reflect" "reflect"
"time" "time"
) )
// After creates a function that invokes func once it's called n or more times // After creates a function that invokes func once it's called n or more times.
// Play: https://go.dev/play/p/8mQhkFmsgqs
func After(n int, fn any) func(args ...any) []reflect.Value { func After(n int, fn any) func(args ...any) []reflect.Value {
// Catch programming error while constructing the closure // Catch programming error while constructing the closure
mustBeFunction(fn) mustBeFunction(fn)
@@ -23,7 +25,8 @@ func After(n int, fn any) func(args ...any) []reflect.Value {
} }
} }
// Before creates a function that invokes func once it's called less than n times // Before creates a function that invokes func once it's called less than n times.
// Play: https://go.dev/play/p/0HqUDIFZ3IL
func Before(n int, fn any) func(args ...any) []reflect.Value { func Before(n int, fn any) func(args ...any) []reflect.Value {
// Catch programming error while constructing the closure // Catch programming error while constructing the closure
mustBeFunction(fn) mustBeFunction(fn)
@@ -40,41 +43,48 @@ func Before(n int, fn any) func(args ...any) []reflect.Value {
} }
} }
// Fn is for curry function which is func(...any) any // CurryFn is for make curry function
type Fn func(...any) any type CurryFn[T any] func(...T) T
// Curry make a curry function // New make a curry function for specific value.
func (f Fn) Curry(i any) func(...any) any { // Play: Todo
return func(values ...any) any { func (cf CurryFn[T]) New(val T) func(...T) T {
v := append([]any{i}, values...) return func(vals ...T) T {
return f(v...) args := append([]T{val}, vals...)
return cf(args...)
} }
} }
// Compose compose the functions from right to left // Compose compose the functions from right to left.
func Compose(fnList ...func(...any) any) func(...any) any { // Play: Todo
return func(s ...any) any { func Compose[T any](fnList ...func(...T) T) func(...T) T {
f := fnList[0] return func(args ...T) T {
restFn := fnList[1:] firstFn := fnList[0]
restFns := fnList[1:]
if len(fnList) == 1 { if len(fnList) == 1 {
return f(s...) return firstFn(args...)
} }
return f(Compose(restFn...)(s...)) fn := Compose(restFns...)
arg := fn(args...)
return firstFn(arg)
} }
} }
// Delay make the function execution after delayed time // Delay make the function execution after delayed time.
// Play: https://go.dev/play/p/Ivtc2ZE-Tye
func Delay(delay time.Duration, fn any, args ...any) { func Delay(delay time.Duration, fn any, args ...any) {
// Catch programming error while constructing the closure // Catch programming error while constructing the closure
mustBeFunction(fn) mustBeFunction(fn)
time.Sleep(delay) time.Sleep(delay)
invokeFunc(fn, args...) unsafeInvokeFunc(fn, args...)
} }
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked. // Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
// Play: https://go.dev/play/p/absuEGB_GN7
func Debounced(fn func(), duration time.Duration) func() { func Debounced(fn func(), duration time.Duration) func() {
// Catch programming error while constructing the closure // Catch programming error while constructing the closure
mustBeFunction(fn) mustBeFunction(fn)
@@ -84,17 +94,16 @@ func Debounced(fn func(), duration time.Duration) func() {
go func() { go func() {
for { for {
select { <-timer.C
case <-timer.C: go fn()
go fn()
}
} }
}() }()
return func() { timer.Reset(duration) } return func() { timer.Reset(duration) }
} }
// Schedule invoke function every duration time, util close the returned bool chan // Schedule invoke function every duration time, util close the returned bool channel.
// Play: https://go.dev/play/p/hbON-Xeyn5N
func Schedule(d time.Duration, fn any, args ...any) chan bool { func Schedule(d time.Duration, fn any, args ...any) chan bool {
// Catch programming error while constructing the closure // Catch programming error while constructing the closure
mustBeFunction(fn) mustBeFunction(fn)
@@ -113,3 +122,40 @@ func Schedule(d time.Duration, fn any, args ...any) chan bool {
return quit return quit
} }
// Pipeline takes a list of functions and returns a function whose param will be passed into
// the functions one by one.
// Play: https://go.dev/play/p/mPdUVvj6HD6
func Pipeline[T any](funcs ...func(T) T) func(T) T {
return func(arg T) (result T) {
result = arg
for _, fn := range funcs {
result = fn(result)
}
return
}
}
func unsafeInvokeFunc(fn any, args ...any) []reflect.Value {
fv := reflect.ValueOf(fn)
params := make([]reflect.Value, len(args))
for i, item := range args {
params[i] = reflect.ValueOf(item)
}
return fv.Call(params)
}
func functionValue(function any) reflect.Value {
v := reflect.ValueOf(function)
if v.Kind() != reflect.Func {
panic(fmt.Sprintf("Invalid function type, value of type %T", function))
}
return v
}
func mustBeFunction(function any) {
v := reflect.ValueOf(function)
if v.Kind() != reflect.Func {
panic(fmt.Sprintf("Invalid function type, value of type %T", function))
}
}

View File

@@ -0,0 +1,147 @@
package function
import (
"fmt"
"strings"
"time"
)
func ExampleAfter() {
fn := After(2, func() {
fmt.Println("test")
})
fn()
fn()
// Output:
// test
}
func ExampleBefore() {
fn := Before(2, func() {
fmt.Println("test")
})
fn()
fn()
fn()
fn()
// Output:
// test
// test
}
func ExampleCurryFn_New() {
add := func(a, b int) int {
return a + b
}
var addCurry CurryFn[int] = func(values ...int) int {
return add(values[0], values[1])
}
add1 := addCurry.New(1)
result := add1(2)
fmt.Println(result)
// Output:
// 3
}
func ExampleCompose() {
toUpper := func(strs ...string) string {
return strings.ToUpper(strs[0])
}
toLower := func(strs ...string) string {
return strings.ToLower(strs[0])
}
transform := Compose(toUpper, toLower)
result := transform("aBCde")
fmt.Println(result)
// Output:
// ABCDE
}
func ExampleDelay() {
var print = func(s string) {
fmt.Println(s)
}
Delay(2*time.Second, print, "hello")
// Output:
// hello
}
func ExampleDebounced() {
count := 0
add := func() {
count++
}
debouncedAdd := Debounced(add, 50*time.Microsecond)
debouncedAdd()
debouncedAdd()
debouncedAdd()
debouncedAdd()
time.Sleep(100 * time.Millisecond)
fmt.Println(count)
debouncedAdd()
time.Sleep(100 * time.Millisecond)
fmt.Println(count)
// Output:
// 1
// 2
}
func ExampleSchedule() {
var result []string
appendFn := func(s string) {
result = append(result, s)
}
stop := Schedule(1*time.Second, appendFn, "*")
time.Sleep(3 * time.Second)
close(stop)
fmt.Println(result)
// Output:
// [* * *]
}
func ExamplePipeline() {
addOne := func(x int) int {
return x + 1
}
double := func(x int) int {
return 2 * x
}
square := func(x int) int {
return x * x
}
fn := Pipeline(addOne, double, square)
result := fn(2)
fmt.Println(result)
// Output:
// 36
}

View File

@@ -1,39 +0,0 @@
package function
import (
"fmt"
"reflect"
)
func invokeFunc(fn any, args ...any) []reflect.Value {
fv := functionValue(fn)
params := make([]reflect.Value, len(args))
for i, item := range args {
params[i] = reflect.ValueOf(item)
}
return fv.Call(params)
}
func unsafeInvokeFunc(fn any, args ...any) []reflect.Value {
fv := reflect.ValueOf(fn)
params := make([]reflect.Value, len(args))
for i, item := range args {
params[i] = reflect.ValueOf(item)
}
return fv.Call(params)
}
func functionValue(function any) reflect.Value {
v := reflect.ValueOf(function)
if v.Kind() != reflect.Func {
panic(fmt.Sprintf("Invalid function type, value of type %T", function))
}
return v
}
func mustBeFunction(function any) {
v := reflect.ValueOf(function)
if v.Kind() != reflect.Func {
panic(fmt.Sprintf("Invalid function type, value of type %T", function))
}
}

View File

@@ -62,24 +62,26 @@ func TestCurry(t *testing.T) {
add := func(a, b int) int { add := func(a, b int) int {
return a + b return a + b
} }
var addCurry Fn = func(values ...any) any { var addCurry CurryFn[int] = func(values ...int) int {
return add(values[0].(int), values[1].(int)) return add(values[0], values[1])
} }
add1 := addCurry.Curry(1) add1 := addCurry.New(1)
assert.Equal(3, add1(2)) assert.Equal(3, add1(2))
} }
func TestCompose(t *testing.T) { func TestCompose(t *testing.T) {
assert := internal.NewAssert(t, "TestCompose") assert := internal.NewAssert(t, "TestCompose")
toUpper := func(a ...any) any { toUpper := func(strs ...string) string {
return strings.ToUpper(a[0].(string)) return strings.ToUpper(strs[0])
} }
toLower := func(a ...any) any { toLower := func(strs ...string) string {
return strings.ToLower(a[0].(string)) return strings.ToLower(strs[0])
} }
expected := toUpper(toLower("aBCde")) expected := toUpper(toLower("aBCde"))
cf := Compose(toUpper, toLower) cf := Compose(toUpper, toLower)
res := cf("aBCde") res := cf("aBCde")
@@ -134,3 +136,21 @@ func TestSchedule(t *testing.T) {
// expected := []string{"*", "*", "*", "*", "*"} // expected := []string{"*", "*", "*", "*", "*"}
// assert.Equal(expected, res) // assert.Equal(expected, res)
} }
func TestPipeline(t *testing.T) {
assert := internal.NewAssert(t, "TestPipeline")
addOne := func(x int) int {
return x + 1
}
double := func(x int) int {
return 2 * x
}
square := func(x int) int {
return x * x
}
f := Pipeline(addOne, double, square)
assert.Equal(36, f(2))
}

View File

@@ -3,12 +3,18 @@ package function
import "time" import "time"
// Watcher is used for record code excution time // Watcher is used for record code excution time
// Play: Todo
type Watcher struct { type Watcher struct {
startTime int64 startTime int64
stopTime int64 stopTime int64
excuting bool excuting bool
} }
// Start the watch timer.
func NewWatcher() *Watcher {
return &Watcher{}
}
// Start the watch timer. // Start the watch timer.
func (w *Watcher) Start() { func (w *Watcher) Start() {
w.startTime = time.Now().UnixNano() w.startTime = time.Now().UnixNano()

View File

@@ -0,0 +1,22 @@
package function
import "fmt"
func ExampleWatcher() {
w := NewWatcher()
w.Start()
longRunningTask()
w.Stop()
// eapsedTime := w.GetElapsedTime().Milliseconds()
fmt.Println("foo")
w.Reset()
// Output:
// foo
}

View File

@@ -9,7 +9,7 @@ import (
func TestWatcher(t *testing.T) { func TestWatcher(t *testing.T) {
assert := internal.NewAssert(t, "TestWatcher") assert := internal.NewAssert(t, "TestWatcher")
w := &Watcher{} w := NewWatcher()
w.Start() w.Start()
longRunningTask() longRunningTask()
@@ -29,9 +29,10 @@ func TestWatcher(t *testing.T) {
assert.Equal(int64(0), w.stopTime) assert.Equal(int64(0), w.stopTime)
} }
func longRunningTask() { func longRunningTask() []int64 {
var slice []int64 var data []int64
for i := 0; i < 10000000; i++ { for i := 0; i < 10000000; i++ {
slice = append(slice, int64(i)) data = append(data, int64(i))
} }
return data
} }

5
go.mod
View File

@@ -1,3 +1,8 @@
module github.com/duke-git/lancet/v2 module github.com/duke-git/lancet/v2
go 1.18 go 1.18
require (
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a
golang.org/x/text v0.5.0
)

4
go.sum Normal file
View File

@@ -0,0 +1,4 @@
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=

207
iterator/iterator.go Normal file
View File

@@ -0,0 +1,207 @@
// 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 package is just a experiment to explore how iterators could work in Go.
// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs.
// 3. It is currently under development, unstable, and will not be completed for some time in the future.
// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it.
// Hope that Go can support iterator in future. see https://github.com/golang/go/discussions/54245 and https://github.com/golang/go/discussions/56413
package iterator
import (
"context"
"golang.org/x/exp/constraints"
)
// 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
}
// 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 constraints.Integer | constraints.Float](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 constraints.Integer | constraints.Float] struct {
start, end, step T
}
func (iter *rangeIterator[T]) HasNext() bool {
return iter.start < iter.end
}
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
}
// 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 FromChannel[T any](channel <-chan T) Iterator[T] {
return &channelIterator[T]{channel: channel}
}
type channelIterator[T any] struct {
channel <-chan T
}
func (iter *channelIterator[T]) Next() (T, bool) {
item, ok := <-iter.channel
return item, ok
}
func (iter *channelIterator[T]) HasNext() bool {
return len(iter.channel) == 0
}
// ToChannel create a new goroutine to pull items from the channel iterator to the returned channel.
func ToChannel[T any](ctx context.Context, iter Iterator[T], buffer int) <-chan T {
result := make(chan T, buffer)
go func() {
defer close(result)
for item, ok := iter.Next(); ok; item, ok = iter.Next() {
select {
case result <- item:
case <-ctx.Done():
return
}
}
}()
return result
}

103
iterator/iterator_test.go Normal file
View File

@@ -0,0 +1,103 @@
// 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 (
"context"
"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)
})
t.Run("slice iterator ToSlice: ", func(t *testing.T) {
iter := FromSlice([]int{1, 2, 3, 4})
item, _ := iter.Next()
assert.Equal(1, item)
data := ToSlice(iter)
assert.Equal([]int{2, 3, 4}, data)
})
}
func TestRangeIterator(t *testing.T) {
assert := internal.NewAssert(t, "TestRangeIterator")
t.Run("range iterator: ", func(t *testing.T) {
iter := FromRange(1, 4, 1)
item, ok := iter.Next()
assert.Equal(1, item)
assert.Equal(true, ok)
item, ok = iter.Next()
assert.Equal(2, item)
assert.Equal(true, ok)
item, ok = iter.Next()
assert.Equal(3, item)
assert.Equal(true, ok)
_, ok = iter.Next()
assert.Equal(false, ok)
assert.Equal(false, iter.HasNext())
})
}
func TestChannelIterator(t *testing.T) {
assert := internal.NewAssert(t, "TestRangeIterator")
iter := FromSlice([]int{1, 2, 3, 4})
ctx, cancel := context.WithCancel(context.Background())
iter = FromChannel(ToChannel(ctx, iter, 0))
item, ok := iter.Next()
assert.Equal(1, item)
assert.Equal(true, ok)
assert.Equal(true, iter.HasNext())
cancel()
_, ok = iter.Next()
assert.Equal(false, ok)
}

Some files were not shown because too many files have changed in this diff Show More