1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-14 17:52:28 +08:00

Compare commits

..

39 Commits

Author SHA1 Message Date
dudaodong
5d6ab72059 release v2.3.3 2024-10-09 15:20:30 +08:00
dudaodong
30eb2c72b0 doc: rename parameter name of Comma 2024-09-27 10:23:29 +08:00
dudaodong
adf18a2e47 fix: fix bug of Comma, issue #248 2024-09-27 10:18:37 +08:00
dudaodong
f99a8ef3cf refactoring: defect #247 2024-09-20 19:29:31 +08:00
Emmanuel Ferdman
fcdf1d5839 doc: update contribution references (#246)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2024-09-19 09:03:50 +08:00
dudaodong
ee0afed963 Merge branch 'v2' into rc 2024-09-16 09:10:23 +08:00
Kyden
1d94896c9b fix(strutil): rename PadStart to Pad. (#245) 2024-09-15 20:29:12 +08:00
dudaodong
69cf9bbcf0 doc: add new functions in system package 2024-09-13 16:21:57 +08:00
dudaodong
2bbcb85286 feat: add GetProcessInfo for system package 2024-09-13 16:10:09 +08:00
dudaodong
e58c9b797b feat: add StartProcess, StopProcess, KillProcess for system package 2024-09-13 15:09:44 +08:00
dudaodong
3e1ac5e0b5 test: remove unstable test item 2024-09-10 15:59:09 +08:00
dudaodong
8869e0440d doc: update doc for GetOrDefault 2024-09-10 15:54:54 +08:00
yunxuan
69f9c74bcb feat(maputil): add GetValue (#243)
* add maputil.GetValue

* rename GetOrDefault
2024-09-10 15:08:36 +08:00
dudaodong
84ebc7ce71 doc: add doc for new functions of release v2.3.3 2024-09-10 14:47:38 +08:00
dudaodong
c745097749 feat: add AesGcmEncrypt and AesGcmDecrypt in cryptor package 2024-09-10 10:37:47 +08:00
dudaodong
ba75e58e5f feat: add datetime package doc 2024-09-09 14:42:01 +08:00
dudaodong
7e85a0ed7d doc: update doc 2024-09-09 11:51:10 +08:00
dudaodong
2268a0312f doc: update doc 2024-09-09 11:49:53 +08:00
dudaodong
da84d95aa3 feat: add GenerateDatetimesBetween in datetime package 2024-09-09 11:35:17 +08:00
dudaodong
48244d6711 feat: add DaysBetween in datetime package 2024-09-09 10:55:18 +08:00
dudaodong
5e3337a52e feat: add TrackFuncTime in datetime package 2024-09-06 15:51:56 +08:00
dudaodong
c3372e18b1 feat: add Frequency in slice package 2024-09-06 15:06:35 +08:00
dudaodong
90e5a0bfb2 feat: add RegexMatchAllGroups 2024-09-06 14:19:57 +08:00
dudaodong
93be25920f feat: add TemplateReplace 2024-09-06 11:04:54 +08:00
dudaodong
f9e5ec9096 feat: add Rotate for string 2024-09-03 16:36:11 +08:00
dudaodong
601df5dc12 feat: add Rotate for string 2024-09-03 16:35:38 +08:00
dudaodong
63216d9b1c feat: add Shuffle for string 2024-09-03 15:54:05 +08:00
dudaodong
c32a19868d refactoring: make test clear 2024-09-03 15:33:32 +08:00
dudaodong
71e914019b feat: add Ellipsis for string 2024-09-03 14:36:12 +08:00
dudaodong
9824db0056 Merge branch 'rc' 2024-09-02 14:31:59 +08:00
dudaodong
ba9188a29a doc: update check link 2024-09-02 14:28:26 +08:00
dudaodong
8625fbd8d3 doc: update doc for ExecCommand function 2024-09-02 11:22:11 +08:00
dudaodong
81b29baf30 doc: add doc for OrderedMap 2024-09-02 11:19:05 +08:00
dudaodong
5a38e34063 Merge branch 'rc' of github.com:duke-git/lancet into rc 2024-08-30 16:55:10 +08:00
DerekTond
159168dd7b fix 删除文档中废弃的RetryDuration函数 (#240)
* fix 修改文档中已经删除的函数RetryDuration,替换为RetryWithLinearBackoff

* fix 删除主文档中的废弃函数

---------

Co-authored-by: dongyue16 <dongyue16@tal.com>
2024-08-29 19:37:42 +08:00
dudaodong
ec092a009a test: we should write clean unit test code 2024-08-28 16:06:08 +08:00
dudaodong
ca40b5d6c6 refactoring: rename SortByKeys to SortByKey 2024-08-28 10:58:14 +08:00
dudaodong
a6d39a3bba feat: add OrderedMap for issue #237 2024-08-28 10:53:53 +08:00
dudaodong
38148978cf refactoring: change unit variable to package internal 2024-08-26 16:25:57 +08:00
51 changed files with 5762 additions and 880 deletions

169
README.md
View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.3.2-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.3-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -24,7 +24,7 @@
## Features
- 👏 Comprehensive, efficient and reusable.
- 💪 600+ go util functions, support string, slice, datetime, net, crypt...
- 💪 700+ go util functions, support string, slice, datetime, net, crypt...
- 💅 Only depends on two kinds of libraries: go standard library and golang.org/x.
- 🌍 Unit test for every exported function.
@@ -38,7 +38,7 @@
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
```
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.3. </b>
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.4. </b>
```go
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x
@@ -362,6 +362,12 @@ import "github.com/duke-git/lancet/v2/cryptor"
- **<big>AesOfbDecrypt</big>** : decrypt byte slice data with key use AES OFB algorithm.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesOfbDecrypt)]
[[play](https://go.dev/play/p/VtHxtkUj-3F)]
- **<big>AesGcmEncrypt</big>** : encrypt byte slice data with key use AES GCM algorithm.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesGcmEncrypt)]
[[play](todo)]
- **<big>AesGcmDecrypt</big>** : decrypt byte slice data with key use AES GCM algorithm.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesGcmDecrypt)]
[[play](todo)]
- **<big>Base64StdEncode</big>** : encode string with base64 encoding.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Base64StdEncode)]
[[play](https://go.dev/play/p/VOaUyQUreoK)]
@@ -603,6 +609,16 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>TimestampNano</big>** : returns current nano second timestamp.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TimestampNano)]
[[play](https://go.dev/play/p/A9Oq_COrcCF)]
- **<big>TrackFuncTime</big>** : tracks function execution time.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TrackFuncTime)]
[[play](todo)]
- **<big>DaysBetween</big>** : returns the number of days between two times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#DaysBetween)]
[[play](todo)]
- **<big>GenerateDatetimesBetween</big>** : returns a slice of strings between two times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GenerateDatetimesBetween)]
[[play](todo)]
<h3 id="datastructure"> 8. Datastructure package contains some common data structure. eg. list, linklist, stack, queue, set, tree, graph. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -747,7 +763,7 @@ import "github.com/duke-git/lancet/v2/formatter"
#### Function list:
- **<big>Comma</big>** : add comma to a number value by every 3 numbers from right, ahead by symbol char.
- **<big>Comma</big>** : add comma to a number value by every 3 numbers from right, ahead by a prefix symbol char.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/formatter.md#Comma)]
[[play](https://go.dev/play/p/eRD5k2vzUVX)]
- **<big>Pretty</big>** : pretty print data to JSON string.
@@ -792,9 +808,15 @@ import "github.com/duke-git/lancet/v2/function"
- **<big>Delay</big>** : call the function after delayed time.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Delay)]
[[play](https://go.dev/play/p/Ivtc2ZE-Tye)]
- **<big>Debounced</big>** : creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
- **<big>Debounced<sup>deprecated</sup></big>** : creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Debounced)]
[[play](https://go.dev/play/p/absuEGB_GN7)]
- **<big>Debounce</big>** : creates a debounced version of the provided function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Debounce)]
[[play](todo)]
- **<big>Throttle</big>** : creates a throttled version of the provided function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Throttle)]
[[play](todo)]
- **<big>Schedule</big>** : invoke function every duration time, util close the returned bool channel.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Schedule)]
[[play](https://go.dev/play/p/hbON-Xeyn5N)]
@@ -913,6 +935,60 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ToSortedSlicesWithComparator</big>** : converts a map to two slices sorted by key and using a custom comparison function: one for the keys and another for the values.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesWithComparator)]
[[play](https://go.dev/play/p/0nlPo6YLdt3)]
- **<big>NewOrderedMap</big>** : creates a new OrderedMap.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewOrderedMap)]
[[play](todo)]
- **<big>OrderedMap_Set</big>** : sets the given key-value pair for ordered map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Set)]
[[play](todo)]
- **<big>OrderedMap_Get</big>** : returns the value for the given key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Get)]
[[play](todo)]
- **<big>OrderedMap_Delete</big>** : deletes the key-value pair for the given key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Delete)]
[[play](todo)]
- **<big>OrderedMap_Clear</big>** : clears the ordered map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Clear)]
[[play](todo)]
- **<big>OrderedMap_Front</big>** : returns the first key-value pair.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Front)]
[[play](todo)]
- **<big>OrderedMap_Back</big>** : returns the last key-value pair.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Back)]
[[play](todo)]
- **<big>OrderedMap_Range</big>** : calls the given function for each key-value pair.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Range)]
[[play](todo)]
- **<big>OrderedMap_Keys</big>** : returns the keys in order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Keys)]
[[play](todo)]
- **<big>OrderedMap_Values</big>** : returns the values in order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Values)]
[[play](todo)]
- **<big>OrderedMap_Elements</big>** : returns the key-value pairs in order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Elements)]
[[play](todo)]
- **<big>OrderedMap_Len</big>** : returns the number of key-value pairs.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Len)]
[[play](todo)]
- **<big>OrderedMap_Contains</big>** : returns true if the given key exists.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Contains)]
[[play](todo)]
- **<big>OrderedMap_Iter</big>** : returns a channel that yields key-value pairs in order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Iter)]
[[play](todo)]
- **<big>OrderedMap_ReverseIter</big>** : returns a channel that yields key-value pairs in reverse order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_ReverseIter)]
[[play](todo)]
- **<big>OrderedMap_SortByKey</big>** : sorts the map by key given less function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_SortByKey)]
[[play](todo)]
- **<big>OrderedMap_MarshalJSON</big>** : implements the json.Marshaler interface.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_MarshalJSON)]
[[play](todo)]
- **<big>OrderedMap_UnmarshalJSON</big>** : implements the json.Unmarshaler interface.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_UnmarshalJSON)]
[[play](todo)]
- **<big>NewConcurrentMap</big>** : creates a ConcurrentMap with specific shard count.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewConcurrentMap)]
[[play](https://go.dev/play/p/3PenTPETJT0)]
@@ -937,6 +1013,13 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ConcurrentMap_Range</big>** : calls iterator sequentially for each key and value present in each of the shards in the map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ConcurrentMap_Range)]
[[play](https://go.dev/play/p/iqcy7P8P0Pr)]
- **<big>SortByKey</big>** : sorts the map by its keys and returns a new map with sorted keys.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#SortByKey)]
[[play](todo)]
- **<big>GetOrDefault</big>** : returns the value of the given key or a default value if the key is not present.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#GetOrDefault)]
[[play](todo)]
<h3 id="mathutil"> 13. Mathutil package implements some functions for math calculation. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1170,17 +1253,33 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>RandUniqueIntSlice</big>** : generate a slice of random int that do not repeat.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandUniqueIntSlice)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandSymbolChar</big>** : Generate a random symbol char of specified length.
- **<big>RandSymbolChar</big>** : generate a random symbol char of specified length.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandSymbolChar)]
[[play](https://go.dev/play/p/Im6ZJxAykOm)]
- **<big>RandFloat</big>** : Generate a random float64 number between [min, max) with specific precision.
- **<big>RandFloat</big>** : generate a random float64 number between [min, max) with specific precision.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloat)]
[[play](https://go.dev/play/p/zbD_tuobJtr)]
- **<big>RandFloats</big>** : Generate a slice of random float64 numbers that do not repeat.
- **<big>RandFloats</big>** : generate a slice of random float64 numbers that do not repeat.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloats)]
[[play](https://go.dev/play/p/I3yndUQ-rhh)]
- **<big>RandStringSlice</big>** : generate a slice of random string of length strLen based on charset.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandStringSlice)]
[[play](todo)]
- **<big>RandBool</big>** : generate a random boolean value (true or false).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandBool)]
[[play](todo)]
- **<big>RandBoolSlice</big>** : generate a random boolean slice of specified length.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandBoolSlice)]
[[play](todo)]
- **<big>RandIntSlice</big>** : generate a slice of random int. Number range in [min, max)
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandIntSlice)]
[[play](todo)]
- **<big>RandFromGivenSlice</big>** : generate a random element from given slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFromGivenSlice)]
[[play](todo)]
- **<big>RandSliceFromGivenSlice</big>** : generate a random slice of length num from given slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandSliceFromGivenSlice)]
[[play](todo)]
<h3 id="retry"> 17. Retry package is for executing a function repeatedly until it was successful or canceled by the context. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1323,6 +1422,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ForEachWithBreak</big>** : iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ForEachWithBreak)]
[[play](https://go.dev/play/p/qScs39f3D9W)]
- **<big>ForEachConcurrent</big>** : applies the iteratee function to each item in the slice concurrently.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ForEachConcurrent)]
[[play](todo)]
- **<big>GroupBy</big>** : iterate over elements of the slice, each element will be group by criteria, returns two slices.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#GroupBy)]
[[play](https://go.dev/play/p/QVkPxzPR0iA)]
@@ -1350,6 +1452,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Map</big>** : creates an slice of values by running each element of slice thru iteratee function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Map)]
[[play](https://go.dev/play/p/biaTefqPquw)]
- **<big>MapConcurrent</big>** : applies the iteratee function to each item in the slice by concrrent.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#MapConcurrent)]
[[play](todo)]
- **<big>Merge</big>** : merge all given slices into one slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Merge)]
[[play](https://go.dev/play/p/lbjFp784r9N)]
@@ -1365,6 +1470,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ReduceRight</big>** : ReduceRight is like ReduceBy, but it iterates over elements of slice from right to left.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ReduceRight)]
[[play](https://go.dev/play/p/qT9dZC03A1K)]
- **<big>ReduceConcurrent</big>** : reduces the slice to a single value by applying the reducer function to each item in the slice concurrently.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ReduceConcurrent)]
[[play](todo)]
- **<big>Replace</big>** : returns a copy of the slice with the first n non-overlapping instances of old replaced by new.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Replace)]
[[play](https://go.dev/play/p/P5mZp7IhOFo)]
@@ -1421,11 +1529,13 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/UR323iZLDpv)]
- **<big>UniqueByComparator</big>** : remove duplicate elements from the input slice using the provided comparator function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByComparator)]
[[play](todo)]
- **<big>UniqueByField</big>** : remove duplicate elements in struct slice by struct field.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByField)]
[[play](https://go.dev/play/p/6cifcZSPIGu)]
- **<big>UniqueByParallel</big>** : remove duplicate elements from the slice by parallel.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByParallel)]
- **<big>UniqueByConcurrent</big>** : remove duplicate elements from the slice by concurrent.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByConcurrent)]
[[play](todo)]
- **<big>Union</big>** : creates a slice of unique elements, in order, from all given slices.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Union)]
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
@@ -1461,6 +1571,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>LeftPadding</big>** : adds padding to the left begin of a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#LeftPadding)]
[[play](https://go.dev/play/p/jlQVoelLl2k)]
- **<big>Frequency</big>** : counts the frequency of each element in the slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Frequency)]
[[play](todo)]
<h3 id="stream"> 19. Stream package implements a sequence of elements supporting sequential and operations. this package is an experiment to explore if stream in go can work as the way java does. its function is very limited. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1495,6 +1608,9 @@ import "github.com/duke-git/lancet/v2/stream"
- **<big>Filter</big>** : returns a stream consisting of the elements of this stream that match the given predicate.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Filter)]
[[play](https://go.dev/play/p/MFlSANo-buc)]
- **<big>FilterConcurrent</big>** : Applies the provided filter function `predicate` to each element of the input slice concurrently.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FilterConcurrent)]
[[play](todo)]
- **<big>Map</big>** : returns a stream consisting of the elements of this stream that apply the given function to elements of stream.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Map)]
[[play](https://go.dev/play/p/OtNQUImdYko)]
@@ -1712,7 +1828,21 @@ import "github.com/duke-git/lancet/v2/strutil"
- **<big>HammingDistance</big>** : calculates the Hamming distance between two strings.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#HammingDistance)]
[[play](https://go.dev/play/p/glNdQEA9HUi)]
- **<big>Concat</big>** : concatenates strings.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Concat)]
[[play](todo)]
- **<big>Ellipsis</big>** : truncates a string to a specified length and appends an ellipsis.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Ellipsis)]
[[play](todo)]
- **<big>Shuffle</big>** : shuffle the order of characters of given string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Shuffle)]
[[play](todo)]
- **<big>Rotate</big>** : rotates the string by the specified number of characters.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Rotate)]
[[play](todo)]
- **<big>TemplateReplace</big>** : replaces the placeholders in the template string with the corresponding values in the data map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#TemplateReplace)]
[[play](todo)]
<h3 id="system"> 22. System package contain some functions about os, runtime, shell command. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1749,6 +1879,19 @@ import "github.com/duke-git/lancet/v2/system"
- **<big>GetOsBits</big>** : return current os bits (32 or 64).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetOsBits)]
[[play](https://go.dev/play/p/ml-_XH3gJbW)]
- **<big>StartProcess</big>** : start a new process with the specified name and arguments.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#StartProcess)]
[[play](todo)]
- **<big>StopProcess</big>** : stop a process by pid.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#StopProcess)]
[[play](todo)]
- **<big>KillProcess</big>** : kill a new process with the specified name and arguments.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#KillProcess)]
[[play](todo)]
- **<big>GetProcessInfo</big>** : retrieves detailed process information by pid.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetProcessInfo)]
[[play](todo)]
<h3 id="tuple"> 23. Tuple package implements tuple data type and some operations on it. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.3.2-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.3-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -23,7 +23,7 @@
## 特性
- 👏 全面、高效、可复用。
- 💪 600+常用 go 工具函数,支持 string、slice、datetime、net、crypt...
- 💪 700+常用 go 工具函数,支持 string、slice、datetime、net、crypt...
- 💅 只依赖 go 标准库和 golang.org/x。
- 🌍 所有导出函数单元测试覆盖率 100%。
@@ -37,7 +37,7 @@
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
```
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.3。</b>
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.4。</b>
```go
go get github.com/duke-git/lancet// 使用go1.18以下版本, 必须安装v1.x.x版本
@@ -363,6 +363,12 @@ import "github.com/duke-git/lancet/v2/cryptor"
- **<big>AesOfbDecrypt</big>** : 使用 AES OFB 算法模式解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesOfbDecrypt)]
[[play](https://go.dev/play/p/VtHxtkUj-3F)]
- **<big>AesGcmEncrypt</big>** : 使用 AES GCM 算法模式加密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesGcmEncrypt)]
[[play](todo)]
- **<big>AesGcmDecrypt</big>** : 使用 AES GCM 算法模式解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesGcmDecrypt)]
[[play](todo)]
- **<big>Base64StdEncode</big>** : 将字符串 base64 编码。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Base64StdEncode)]
[[play](https://go.dev/play/p/VOaUyQUreoK)]
@@ -472,7 +478,7 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/sSVmkfENKMz)]
<h3 id="datetime"> 7. datetime 日期时间处理包,格式化日期,比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="datetime"> 7. datetime日期时间处理包格式化日期比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/datetime"
@@ -606,6 +612,16 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>TimestampNano</big>** : 返回当前纳秒级时间戳。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#TimestampNano)]
[[play](https://go.dev/play/p/A9Oq_COrcCF)]
- **<big>TrackFuncTime</big>** : 测试函数执行时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#TrackFuncTime)]
[[play](todo)]
- **<big>DaysBetween</big>** : 返回两个日期之间的天数差。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#DaysBetween)]
[[play](todo)]
- **<big>GenerateDatetimesBetween</big>** : 生成从start到end的所有日期时间的字符串列表。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GenerateDatetimesBetween)]
[[play](todo)]
<h3 id="datastructure"> 8. datastructure 包含一些普通的数据结构实现。例如list, linklist, stack, queue, set, tree, graph。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -748,7 +764,7 @@ import "github.com/duke-git/lancet/v2/formatter"
#### 函数列表:
- **<big>Comma</big>** : 用逗号每隔 3 位分割数字/字符串,支持前缀添加符号。
- **<big>Comma</big>** : 用逗号每隔 3 位分割数字/字符串,支持添加前缀符号。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#Comma)]
[[play](https://go.dev/play/p/eRD5k2vzUVX)]
- **<big>Pretty</big>** : 返回 pretty JSON 字符串。
@@ -793,9 +809,15 @@ import "github.com/duke-git/lancet/v2/function"
- **<big>Delay</big>** : 延迟 delay 时间后调用函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Delay)]
[[play](https://go.dev/play/p/Ivtc2ZE-Tye)]
- **<big>Debounced</big>** : 创建一个 debounced 函数,该函数延迟调用 fn 直到自上次调用 debounced 函数后等待持续时间过去。
- **<big>Debounced<sup>deprecated</sup></big>** : 创建一个 debounced 函数,该函数延迟调用 fn 直到自上次调用 debounced 函数后等待持续时间过去。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Debounced)]
[[play](https://go.dev/play/p/absuEGB_GN7)]
- **<big>Debounce</big>** : 创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Debounce)]
[[play](todo)]
- **<big>Throttle</big>** : 创建一个函数的节流版本。返回的函数保证在每个时间间隔内最多只会被调用一次。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Throttle)]
[[play](todo)]
- **<big>Schedule</big>** : 每次持续时间调用函数,直到关闭返回的 channel。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Schedule)]
[[play](https://go.dev/play/p/hbON-Xeyn5N)]
@@ -915,6 +937,60 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ToSortedSlicesWithComparator</big>** : 将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片value切片中元素的位置与key对应。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesWithComparator)]
[[play](https://go.dev/play/p/0nlPo6YLdt3)]
- **<big>NewOrderedMap</big>** : 创建有序映射。有序映射是键值对的集合,其中键是唯一的,并且保留键插入的顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewOrderedMap)]
[[play](todo)]
- **<big>OrderedMap_Set</big>** : 设置给定的键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Set)]
[[play](todo)]
- **<big>OrderedMap_Get</big>** : 返回给定键的值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Get)]
[[play](todo)]
- **<big>OrderedMap_Delete</big>** : 删除给定键的键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Delete)]
[[play](todo)]
- **<big>OrderedMap_Clear</big>** : 清空map数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Clear)]
[[play](todo)]
- **<big>OrderedMap_Front</big>** : 返回第一个键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Front)]
[[play](todo)]
- **<big>OrderedMap_Back</big>** : 返回最后一个键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Back)]
[[play](todo)]
- **<big>OrderedMap_Range</big>** : 为每个键值对调用给定的函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Range)]
[[play](todo)]
- **<big>OrderedMap_Keys</big>** : 按顺序返回键的切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Keys)]
[[play](todo)]
- **<big>OrderedMap_Values</big>** : 按顺序返回值的切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Values)]
[[play](todo)]
- **<big>OrderedMap_Elements</big>** : 按顺序返回键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Elements)]
[[play](todo)]
- **<big>OrderedMap_Len</big>** : 返回键值对的数量。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Len)]
[[play](todo)]
- **<big>OrderedMap_Contains</big>** : 如果给定的键存在则返回true。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Contains)]
[[play](todo)]
- **<big>OrderedMap_Iter</big>** : 返回按顺序产生键值对的通道。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Iter)]
[[play](todo)]
- **<big>OrderedMap_ReverseIter</big>** : 返回以相反顺序产生键值对的通道。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_ReverseIter)]
[[play](todo)]
- **<big>OrderedMap_SortByKey</big>** : 使用传入的比较函数排序map key。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_SortByKey)]
[[play](todo)]
- **<big>OrderedMap_MarshalJSON</big>** : 实现json.Marshaler接口。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_MarshalJSON)]
[[play](todo)]
- **<big>OrderedMap_UnmarshalJSON</big>** : 实现json.Unmarshaler接口。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_UnmarshalJSON)]
[[play](todo)]
- **<big>NewConcurrentMap</big>** : ConcurrentMap 协程安全的 map 结构。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewConcurrentMap)]
[[play](https://go.dev/play/p/3PenTPETJT0)]
@@ -939,6 +1015,12 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ConcurrentMap_Range</big>** : 为 map 中每个键和值顺序调用迭代器。 如果 iterator 返回 false则停止迭代。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_Range)]
[[play](https://go.dev/play/p/iqcy7P8P0Pr)]
- **<big>SortByKey</big>** : 对传入的map根据key进行排序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#SortByKey)]
[[play](todo)]
- **<big>GetOrDefault</big>** : 返回给定键的值,如果键不存在,则返回默认值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrDefault)]
[[play](todo)]
<h3 id="mathutil"> 13. mathutil 包实现了一些数学计算的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1181,6 +1263,24 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>RandFloats</big>** : 生成随机float64数字切片可以指定长度范围和精度.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloats)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandStringSlice</big>** : 生成随机字符串slice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandStringSlice)]
[[play](todo)]
- **<big>RandBool</big>** : 生成随机bool值(true or false)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBool)]
[[play](todo)]
- **<big>RandBoolSlice</big>** : 生成特定长度的随机bool slice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBoolSlice)]
[[play](todo)]
- **<big>RandIntSlice</big>** : 生成一个特定长度的随机int切片数值范围[min, max)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandIntSlice)]
[[play](todo)]
- **<big>RandFromGivenSlice</big>** : 从给定切片中随机生成元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFromGivenSlice)]
[[play](todo)]
- **<big>RandSliceFromGivenSlice</big>** : 从给定切片中生成长度为 num 的随机切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandSliceFromGivenSlice)]
[[play](todo)]
<h3 id="retry"> 17. retry 重试执行函数直到函数运行成功或被 context cancel。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1200,9 +1300,6 @@ import "github.com/duke-git/lancet/v2/retry"
- **<big>RetryFunc</big>** : 重试执行的函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryFunc)]
[[play](https://go.dev/play/p/nk2XRmagfVF)]
- **<big>RetryDuration</big>** : 设置重试间隔时间,默认 3 秒。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryDuration)]
[[play](https://go.dev/play/p/nk2XRmagfVF)]
- **<big>RetryTimes</big>** : 设置重试次数,默认 5。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryTimes)]
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
@@ -1321,6 +1418,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ForEach</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEach)]
[[play](https://go.dev/play/p/DrPaa4YsHRF)]
- **<big>ForEachConcurrent</big>** : 对slice并发执行foreach操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEachConcurrent)]
[[play](todo)]
- **<big>ForEachWithBreak</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数,当 iteratee 函数返回 false 时,终止遍历。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEachWithBreak)]
[[play](https://go.dev/play/p/qScs39f3D9W)]
@@ -1351,6 +1451,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Map</big>** : 对 slice 中的每个元素执行 map 函数以创建一个新切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Map)]
[[play](https://go.dev/play/p/biaTefqPquw)]
- **<big>MapConcurrent</big>** : 对slice并发执行map操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#MapConcurrent)]
[[play](todo)]
- **<big>Merge</big>** : 合并多个切片(不会消除重复元素)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Merge)]
[[play](https://go.dev/play/p/lbjFp784r9N)]
@@ -1366,6 +1469,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ReduceRight</big>** : 类似 ReduceBy 操作,迭代切片元素顺序从右至左。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceRight)]
[[play](https://go.dev/play/p/qT9dZC03A1K)]
- **<big>ReduceConcurrent</big>** : 对切片元素执行并发reduce操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceConcurrent)]
[[play](todo)]
- **<big>Replace</big>** : 返回切片的副本,其中前 n 个不重叠的 old 替换为 new。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Replace)]
[[play](https://go.dev/play/p/P5mZp7IhOFo)]
@@ -1422,11 +1528,13 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/UR323iZLDpv)]
- **<big>UniqueByComparator</big>** : 使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByComparator)]
[[play](todo)]
- **<big>UniqueByField</big>** : 根据struct字段对struct切片去重复。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByField)]
[[play](https://go.dev/play/p/6cifcZSPIGu)]
- **<big>UniqueByParallel</big>** : 并发的从输入切片中移除重复元素,结果保持元素的顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByParallel)]
- **<big>UniqueByConcurrent</big>** : 并发的从输入切片中移除重复元素,结果保持元素的顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByConcurrent)]
[[play](todo)]
- **<big>Union</big>** : 合并多个切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Union)]
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
@@ -1461,7 +1569,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>LeftPadding</big>** : 在切片的左部添加元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#LeftPadding)]
[[play](https://go.dev/play/p/jlQVoelLl2k)]
- **<big>Frequency</big>** : 计算切片中每个元素出现的频率。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Frequency)]
[[play](todo)]
<h3 id="stream"> 19. stream 流,该包仅验证简单的 stream 实现,功能有限。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1490,12 +1600,15 @@ import "github.com/duke-git/lancet/v2/stream"
- **<big>Concat</big>** : 创建一个延迟连接 stream其元素是第一个 stream 的所有元素,后跟第二个 stream 的全部元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Concat)]
[[play](https://go.dev/play/p/HM4OlYk_OUC)]
- **<big>Distinct</big>** : 创建并返回一个 stream用于删除重复的项。
- **<big>Distinct</big>** : 创建并返回一个stream用于删除重复的项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Distinct)]
[[play](https://go.dev/play/p/eGkOSrm64cB)]
- **<big>Filter</big>** : 返回一个通过判定函数的 stream。
- **<big>Filter</big>** : 返回一个通过判定函数的stream。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Filter)]
[[play](https://go.dev/play/p/MFlSANo-buc)]
- **<big>FilterConcurrent</big>** : 对slice并发执行filter操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#FilterConcurrent)]
[[play](todo)]
- **<big>Map</big>** : 返回一个 stream该 stream 由将给定函数应用于源 stream 元素的元素组成。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Map)]
[[play](https://go.dev/play/p/OtNQUImdYko)]
@@ -1717,7 +1830,21 @@ import "github.com/duke-git/lancet/v2/strutil"
- **<big>HammingDistance</big>** : 计算两个字符串之间的汉明距离。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HammingDistance)]
[[play](https://go.dev/play/p/glNdQEA9HUi)]
- **<big>Concat</big>** : 拼接字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Concat)]
[[play](todo)]
- **<big>Ellipsis</big>** : 将字符串截断到指定长度,并在末尾添加省略号。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Ellipsis)]
[[play](todo)]
- **<big>Shuffle</big>** : 打乱给定字符串中的字符顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Shuffle)]
[[play](todo)]
- **<big>Rotate</big>** : 按指定的字符数旋转字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Rotate)]
[[play](todo)]
- **<big>TemplateReplace</big>** : 将模板字符串中的占位符替换为map中的相应值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#TemplateReplace)]
[[play](todo)]
<h3 id="system"> 22. system 包含 os, runtime, shell command 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1754,6 +1881,20 @@ import "github.com/duke-git/lancet/v2/system"
- **<big>GetOsBits</big>** : 获取当前操作系统位数(32/64)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#GetOsBits)]
[[play](https://go.dev/play/p/ml-_XH3gJbW)]
- **<big>StartProcess</big>** :创建进程。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#StartProcess)]
[[play](todo)]
- **<big>StopProcess</big>** : 停止进程。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#StopProcess)]
[[play](todo)]
- **<big>KillProcess</big>** : 杀掉进程。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#KillProcess)]
[[play](todo)]
- **<big>GetProcessInfo</big>** : 根据进程id获取进程信息。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#GetProcessInfo)]
[[play](todo)]
<h3 id="tuple"> 23. Tuple 包实现一个元组数据类型。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1989,7 +2130,7 @@ import "github.com/duke-git/lancet/v2/validator"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsBase64URL)]
[[play](https://go.dev/play/p/vhl4mr8GZ6S)]
- **<big>IsJWT</big>** : 检查字符串是否是有效的 JSON Web Token (JWT)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsJWT)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsJWT)]
[[play](https://go.dev/play/p/R6Op7heJbKI)]
- **<big>IsVisa</big>** : 检查字符串是否是有效的 visa 卡号。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsVisa)]

View File

@@ -244,6 +244,56 @@ func AesOfbDecrypt(data, key []byte) []byte {
return decrypted
}
// AesGcmEncrypt encrypt data with key use AES GCM algorithm
// Play: todo
func AesGcmEncrypt(data, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err)
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
panic(err)
}
ciphertext := gcm.Seal(nonce, nonce, data, nil)
return ciphertext
}
// AesGcmDecrypt decrypt data with key use AES GCM algorithm
// Play: todo
func AesGcmDecrypt(data, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err)
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
panic("ciphertext too short")
}
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
panic(err)
}
return plaintext
}
// DesEcbEncrypt encrypt data with key use DES ECB algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/8qivmPeZy4P

View File

@@ -129,6 +129,34 @@ func ExampleAesOfbDecrypt() {
// hello
}
func ExampleAesGcmEncrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesGcmEncrypt([]byte(data), []byte(key))
decrypted := AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleAesGcmDecrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesGcmEncrypt([]byte(data), []byte(key))
decrypted := AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesEcbEncrypt() {
data := "hello"
key := "abcdefgh"

View File

@@ -168,3 +168,17 @@ func TestRsaEncryptOAEP(t *testing.T) {
assert.IsNil(err)
assert.Equal("hello world", string(decrypted))
}
func TestAesGcmEncrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefghijklmnop"
encrypted := AesGcmEncrypt([]byte(data), []byte(key))
decrypted := AesGcmDecrypt(encrypted, []byte(key))
assert := internal.NewAssert(t, "TestAesGcmEncrypt")
assert.Equal(data, string(decrypted))
}

View File

@@ -30,6 +30,7 @@ package datetime
import (
"fmt"
"runtime"
"strings"
"time"
)
@@ -382,11 +383,63 @@ func TimestampNano(timezone ...string) int64 {
return t.UnixNano()
}
// TraceFuncTime: trace the func costed time,just call it at top of the func like `defer TraceFuncTime()()`
func TraceFuncTime() func() {
pre := time.Now()
// TrackFuncTime track the time of function execution.
// call it at top of the func like `defer TrackFuncTime(time.Now())()`
// Play: todo
func TrackFuncTime(pre time.Time) func() {
callerName := getCallerName()
return func() {
elapsed := time.Since(pre)
fmt.Println("Costs Time:\t", elapsed)
fmt.Printf("Function %s execution time:\t %v", callerName, elapsed)
}
}
func getCallerName() string {
pc, _, _, ok := runtime.Caller(2)
if !ok {
return "Unknown"
}
fn := runtime.FuncForPC(pc)
if fn == nil {
return "Unknown"
}
fullName := fn.Name()
if lastDot := strings.LastIndex(fullName, "."); lastDot != -1 {
return fullName[lastDot+1:]
}
return fullName
}
// DaysBetween returns the number of days between two times.
// Play: todo
func DaysBetween(start, end time.Time) int {
duration := end.Sub(start)
days := int(duration.Hours() / 24)
return days
}
// GenerateDatetimesBetween returns a slice of strings between two times.
// layout: the format of the datetime string
// interval: the interval between two datetimes
// Play: todo
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error) {
var result []string
if start.After(end) {
start, end = end, start
}
duration, err := time.ParseDuration(interval)
if err != nil {
return nil, err
}
for current := start; !current.After(end); current = current.Add(duration) {
result = append(result, current.Format(layout))
}
return result, nil
}

View File

@@ -408,3 +408,32 @@ func ExampleIsWeekend() {
// true
// false
}
func ExampleDaysBetween() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
result := DaysBetween(start, end)
fmt.Println(result)
// Output:
// 9
}
func ExampleGenerateDatetimesBetween() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
layout := "2006-01-02 15:04:05"
interval := "1h"
result, err := GenerateDatetimesBetween(start, end, layout, interval)
fmt.Println(result)
fmt.Println(err)
// Output:
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
// <nil>
}

View File

@@ -410,3 +410,114 @@ func TestTimestamp(t *testing.T) {
ts4 := TimestampNano()
t.Log(ts4)
}
func TestTrackFuncTime(t *testing.T) {
defer TrackFuncTime(time.Now())()
var n int
for i := 0; i < 5000000; i++ {
n++
}
}
func TestDaysBetween(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDaysBetween")
tests := []struct {
start time.Time
end time.Time
expected int
}{
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC),
expected: 9,
},
{
start: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
expected: -9,
},
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
expected: 0,
},
{
start: time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.December, 31, 0, 0, 0, 0, time.UTC),
expected: 365,
},
{
start: time.Date(2024, time.March, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.March, 31, 0, 0, 0, 0, time.UTC),
expected: 30,
},
}
for _, tt := range tests {
result := DaysBetween(tt.start, tt.end)
assert.Equal(tt.expected, result)
}
}
func TestGenerateDatetimesBetween(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGenerateDatetimesBetween")
tests := []struct {
start time.Time
end time.Time
layout string
interval string
expected []string
}{
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC),
layout: "2006-01-02 15:04:05",
interval: "30m",
expected: []string{
"2024-09-01 00:00:00",
"2024-09-01 00:30:00",
"2024-09-01 01:00:00",
"2024-09-01 01:30:00",
"2024-09-01 02:00:00",
},
},
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
layout: "2006-01-02 15:04:05",
interval: "1h",
expected: []string{"2024-09-01 00:00:00"},
},
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 3, 0, 0, 0, time.UTC),
layout: "2006-01-02 15:04:05",
interval: "2h",
expected: []string{
"2024-09-01 00:00:00",
"2024-09-01 02:00:00",
},
},
}
for _, tt := range tests {
result, err := GenerateDatetimesBetween(tt.start, tt.end, tt.layout, tt.interval)
assert.Equal(tt.expected, result)
assert.IsNil(err)
}
t.Run("Invalid interval", func(t *testing.T) {
_, err := GenerateDatetimesBetween(time.Now(), time.Now(), "2006-01-02 15:04:05", "invalid")
if err == nil {
t.Fatal("Expected error, got nil")
}
})
}

View File

@@ -84,7 +84,7 @@ export const commonConfig = defineConfig({
footer: {
copyright: 'Copyright © 2023-present Duke Du',
message: '备案号: 京ICP备2023022770号',
message: '<a href="https://beian.miit.gov.cn/" target="_blank">京ICP备2023022770号-1</a>',
},
},
})

View File

@@ -42,7 +42,7 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: 'Contribution',
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTING.en-US.md',
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTION.md',
},
],
},

View File

@@ -51,7 +51,7 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: '参与贡献',
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTING.zh-CN.md',
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTION.zh-CN.md',
},
],
},

View File

@@ -32,6 +32,8 @@ import (
- [AesCfbDecrypt](#AesCfbDecrypt)
- [AesOfbEncrypt](#AesOfbEncrypt)
- [AesOfbDecrypt](#AesOfbDecrypt)
- [AesGcmEncrypt](#AesGcmEncrypt)
- [AesGcmDecrypt](#AesGcmDecrypt)
- [Base64StdEncode](#Base64StdEncode)
- [Base64StdDecode](#Base64StdDecode)
- [DesEcbEncrypt](#DesEcbEncrypt)
@@ -379,6 +381,74 @@ func main() {
}
```
### <span id="AesGcmEncrypt">AesGcmEncrypt</span>
<p>使用AES GCM算法模式加密数据。</p>
<b>函数签名:</b>
```go
func AesGcmEncrypt(data, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="AesGcmDecrypt">AesGcmDecrypt</span>
<p>使用AES GCM算法解密数据。</p>
<b>函数签名:</b>
```go
func AesGcmDecrypt(data, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="Base64StdEncode">Base64StdEncode</span>
<p>将字符串base64编码。</p>

View File

@@ -64,6 +64,9 @@ import (
- [TimestampMilli](#TimestampMilli)
- [TimestampMicro](#TimestampMicro)
- [TimestampNano](#TimestampNano)
- [TrackFuncTime](#TrackFuncTime)
- [DaysBetween](#DaysBetween)
- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
<div STYLE="page-break-after: always;"></div>
@@ -1334,7 +1337,7 @@ import (
func main() {
result1 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss")
result2 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss", "EST")
result2 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss", "EST")
fmt.Println(result1)
fmt.Println(result2)
@@ -1464,4 +1467,107 @@ func main() {
// Output:
// 1690363051331788000
}
```
### <span id="TrackFuncTime">TrackFuncTime</span>
<p>测试函数执行时间。</p>
<b>函数签名:</b>
```go
func TrackFuncTime(pre time.Time) func()
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
defer datetime.TrackFuncTime(time.Now())()
var n int
for i := 0; i < 5000000; i++ {
n++
}
fmt.Println(1) // Function main execution time: 1.460287ms
}
```
### <span id="DaysBetween">DaysBetween</span>
<p>返回两个日期之间的天数差。</p>
<b>函数签名:</b>
```go
func DaysBetween(start, end time.Time) int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
result := datetime.DaysBetween(start, end)
fmt.Println(result)
// Output:
// 9
}
```
### <span id="GenerateDatetimesBetween">GenerateDatetimesBetween</span>
<p>生成从start到end的所有日期时间的字符串列表。layout参数表示时间格式例如"2006-01-02 15:04:05"interval参数表示时间间隔例如"1h"表示1小时"30m"表示30分钟。</p>
<b>函数签名:</b>
```go
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
layout := "2006-01-02 15:04:05"
interval := "1h"
result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval)
fmt.Println(result)
fmt.Println(err)
// Output:
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
// <nil>
}
```

View File

@@ -37,12 +37,12 @@ import (
### <span id="Comma">Comma</span>
<p>用逗号每隔3位分割数字/字符串,支持前缀添加符号。参数value必须是数字或者可以转为数字的字符串, 否则返回空字符串</p>
<p>用逗号每隔3位分割数字/字符串,支持添加前缀符号。参数value必须是数字或者可以转为数字的字符串, 否则返回空字符串</p>
<b>函数签名:</b>
```go
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string
func Comma[T constraints.Float | constraints.Integer | string](value T, prefixSymbol string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/eRD5k2vzUVX)</span></b>

View File

@@ -205,7 +205,7 @@ func main() {
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
@@ -753,7 +753,7 @@ func main() {
func Throttle(fn func(), interval time.Duration) func()
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main

View File

@@ -7,6 +7,8 @@ maputil 包包括一些操作 map 的函数。
## 源码:
- [https://github.com/duke-git/lancet/blob/main/maputil/map.go](https://github.com/duke-git/lancet/blob/main/maputil/map.go)
- [https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go](https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go)
- [https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go](https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go)
<div STYLE="page-break-after: always;"></div>
@@ -47,6 +49,24 @@ import (
- [MapToStruct](#MapToStruct)
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
- [NewOrderedMap](#NewOrderedMap)
- [OrderedMap_Set](#OrderedMap_Set)
- [OrderedMap_Get](#OrderedMap_Get)
- [OrderedMap_Front](#OrderedMap_Front)
- [OrderedMap_Back](#OrderedMap_Back)
- [OrderedMap_Delete](#OrderedMap_Delete)
- [OrderedMap_Clear](#OrderedMap_Clear)
- [OrderedMap_Len](#OrderedMap_Len)
- [OrderedMap_Keys](#OrderedMap_Keys)
- [OrderedMap_Values](#OrderedMap_Values)
- [OrderedMap_Contains](#OrderedMap_Contains)
- [OrderedMap_Range](#OrderedMap_Range)
- [OrderedMap_Elements](#OrderedMap_Elements)
- [OrderedMap_Iter](#OrderedMap_Iter)
- [OrderedMap_ReverseIter](#OrderedMap_ReverseIter)
- [OrderedMap_SortByKey](#OrderedMap_SortByKey)
- [OrderedMap_MarshalJSON](#OrderedMap_MarshalJSON)
- [OrderedMap_UnmarshalJSON](#OrderedMap_UnmarshalJSON)
- [NewConcurrentMap](#NewConcurrentMap)
- [ConcurrentMap_Get](#ConcurrentMap_Get)
- [ConcurrentMap_Set](#ConcurrentMap_Set)
@@ -56,7 +76,8 @@ import (
- [ConcurrentMap_Has](#ConcurrentMap_Has)
- [ConcurrentMap_Range](#ConcurrentMap_Range)
- [GetOrSet](#GetOrSet)
- [SortByKeys](#SortByKeys)
- [SortByKey](#SortByKey)
- [GetOrDefault](#GetOrDefault)
<div STYLE="page-break-after: always;"></div>
@@ -1121,6 +1142,689 @@ func main() {
}
```
### <span id="NewOrderedMap">NewOrderedMap</span>
<p>创建有序映射。有序映射是键值对的集合,其中键是唯一的,并且保留键插入的顺序。</p>
<b>函数签名:</b>
```go
func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V]
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Set">OrderedMap_Set</span>
<p>设置给定的键值对。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Set(key K, value V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Get">OrderedMap_Get</span>
<p>返回给定键的值。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Get(key K) (V, bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Delete">OrderedMap_Delete</span>
<p>删除给定键的键值对。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Delete(key K)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Delete("b")
fmt.Println(om.Keys())
// Output:
// [a c]
}
```
### <span id="OrderedMap_Clear">OrderedMap_Clear</span>
<p>清空map数据。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Clear()
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Clear()
fmt.Println(om.Keys())
// Output:
// []
}
```
### <span id="OrderedMap_Front">OrderedMap_Front</span>
<p>返回第一个键值对。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Front() (struct {
Key K
Value V
}, bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
frontElement, ok := om.Front()
fmt.Println(frontElement)
fmt.Println(ok)
// Output:
// {a 1}
// true
}
```
### <span id="OrderedMap_Back">OrderedMap_Back</span>
<p>返回最后一个键值对。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Back() (struct {
Key K
Value V
}, bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
frontElement, ok := om.Front()
fmt.Println(frontElement)
fmt.Println(ok)
// Output:
// {a 1}
// true
}
```
### <span id="OrderedMap_Range">OrderedMap_Range</span>
<p>为每个键值对调用给定的函数。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Range(func(key string, value int) bool {
fmt.Println(key, value)
return true
})
// Output:
// a 1
// b 2
// c 3
}
```
### <span id="OrderedMap_Keys">OrderedMap_Keys</span>
<p>按顺序返回键的切片。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Keys() []K
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
keys := om.Keys()
fmt.Println(keys)
// Output:
// [a b c]
}
```
### <span id="OrderedMap_Values">OrderedMap_Values</span>
<p>按顺序返回值的切片。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Values() []V
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
values := om.Values()
fmt.Println(values)
// Output:
// [1 2 3]
}
```
### <span id="OrderedMap_Elements">OrderedMap_Elements</span>
<p>按顺序返回键值对。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Elements() []struct
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
elements := om.Elements()
fmt.Println(elements)
// Output:
// [{a 1} {b 2} {c 3}]
}
```
### <span id="OrderedMap_Len">OrderedMap_Len</span>
<p>返回键值对的数量。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Len() int
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Len()
fmt.Println(om.Len())
// Output:
// 3
}
```
### <span id="OrderedMap_Contains">OrderedMap_Contains</span>
<p>如果给定的键存在则返回true。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Contains(key K) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
result1 := om.Contains("a")
result2 := om.Contains("d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="OrderedMap_Iter">OrderedMap_Iter</span>
<p>返回按顺序产生键值对的通道。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Iter() <-chan struct {
Key K
Value V
}
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.Iter() {
fmt.Println(elem)
}
// Output:
// {a 1}
// {b 2}
// {c 3}
}
```
### <span id="OrderedMap_ReverseIter">OrderedMap_ReverseIter</span>
<p>返回以相反顺序产生键值对的通道。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) ReverseIter() <-chan struct {
Key K
Value V
}
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.ReverseIter() {
fmt.Println(elem)
}
// Output:
// {c 3}
// {b 2}
// {a 1}
}
```
### <span id="OrderedMap_SortByKey">OrderedMap_SortByKey</span>
<p>使用传入的比较函数排序map key。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
om.SortByKey(func(a, b int) bool {
return a < b
})
fmt.Println(om.Elements())
// Output:
// [{1 a} {2 b} {3 c} {4 d}]
}
```
### <span id="OrderedMap_MarshalJSON">OrderedMap_MarshalJSON</span>
<p>实现json.Marshaler接口。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
b, _ := om.MarshalJSON()
fmt.Println(string(b))
// Output:
// {"a":1,"b":2,"c":3}
}
```
### <span id="OrderedMap_UnmarshalJSON">OrderedMap_UnmarshalJSON</span>
<p>实现json.Unmarshaler接口。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
data := []byte(`{"a":1,"b":2,"c":3}`)
om.UnmarshalJSON(data)
fmt.Println(om.Elements())
// Output:
}
```
### <span id="NewConcurrentMap">NewConcurrentMap</span>
<p>ConcurrentMap协程安全的map结构。</p>
@@ -1525,17 +2229,17 @@ func main() {
}
```
### <span id="SortByKeys">SortByKeys</span>
### <span id="SortByKey">SortByKey</span>
<p>对传入的map根据key进行排序返回排序后的map。</p>
<b>函数签名:</b>
```go
func SortByKeys[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
@@ -1553,11 +2257,51 @@ func main() {
2: "b",
}
result := maputil.SortByKeys(m)
result := maputil.SortByKey(m)
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
```
### <span id="GetOrDefault">GetOrDefault</span>
<p>返回给定键的值,如果键不存在,则返回默认值。</p>
<b>函数签名:</b>
```go
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result1 := maputil.GetOrDefault(m, 1, "default")
result2 := maputil.GetOrDefault(m, 6, "default")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// a
// default
}
```

View File

@@ -126,7 +126,7 @@ func main() {
### <span id="RandFromGivenSlice">RandFromGivenSlice</span>
<p>从给定切片中随机生成元素</p>
<p>从给定切片中随机生成元素</p>
<b>函数签名:</b>
@@ -134,7 +134,7 @@ func main() {
func RandFromGivenSlice[T any](slice []T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
@@ -153,7 +153,7 @@ func main() {
### <span id="RandSliceFromGivenSlice">RandSliceFromGivenSlice</span>
<p>从给定切片中生成长度为 num 的随机切片</p>
<p>从给定切片中生成长度为 num 的随机切片</p>
<b>函数签名:</b>
@@ -161,7 +161,7 @@ func main() {
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
@@ -451,7 +451,7 @@ func main() {
func RandStringSlice(charset string, sliceLen, strLen int) []string
```
<b>实例:</b>
<b>实例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
@@ -480,7 +480,7 @@ func main() {
func RandBool() bool
```
<b>实例:</b>
<b>实例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
@@ -506,7 +506,7 @@ func main() {
func RandBoolSlice(length int) []bool
```
<b>实例:</b>
<b>实例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main

View File

@@ -70,7 +70,7 @@ func main() {
return errors.New("error occurs")
}
duration := retry.RetryDuration(time.Microsecond*50)
duration := retry.RetryWithLinearBackoff(time.Microsecond*50)
retry.Retry(increaseNumber,
duration,
@@ -116,7 +116,7 @@ func main() {
return errors.New("error occurs")
}
duration := retry.RetryDuration(time.Microsecond*50)
duration := retry.RetryWithLinearBackoff(time.Microsecond*50)
err := retry.Retry(increaseNumber, duration)
if err != nil {
@@ -173,52 +173,6 @@ func main() {
}
```
### <span id="RetryDuration">RetryDuration</span>
<p>设置重试间隔时间默认3秒</p>
<b>函数签名:</b>
```go
func RetryDuration(d time.Duration)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/nk2XRmagfVF)</span></b>
```go
package main
import (
"fmt"
"errors"
"log"
"github.com/duke-git/lancet/v2/retry"
)
func main() {
number := 0
increaseNumber := func() error {
number++
if number == 3 {
return nil
}
return errors.New("error occurs")
}
duration := retry.RetryDuration(time.Microsecond*50)
err := retry.Retry(increaseNumber, duration)
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
```
### <span id="Retry">Retry</span>
<p>重试执行函数retryFunc直到函数运行成功或被context停止</p>
@@ -251,7 +205,7 @@ func main() {
return errors.New("error occurs")
}
duration := retry.RetryDuration(time.Microsecond*50)
duration := retry.RetryWithLinearBackoff(time.Microsecond*50)
err := retry.Retry(increaseNumber, duration)
if err != nil {

View File

@@ -105,6 +105,7 @@ import (
- [Break](#Break)
- [RightPadding](#RightPadding)
- [LeftPadding](#LeftPadding)
- [Frequency](#Frequency)
<div STYLE="page-break-after: always;"></div>
@@ -914,7 +915,7 @@ func main() {
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
@@ -1569,7 +1570,7 @@ func main() {
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
@@ -1770,7 +1771,7 @@ func main() {
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
@@ -2902,7 +2903,7 @@ func main() {
}
```
<span id="RightPadding">RightPadding</span>
### <span id="RightPadding">RightPadding</span>
<p>在切片的右部添加元素。</p>
@@ -2921,7 +2922,7 @@ import (
)
func main() {
nums := []int{1, 2, 3, 4, 5}
nums := []int{1, 2, 3, 4, 5}
padded := slice.RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
@@ -2929,7 +2930,7 @@ func main() {
}
```
<span id="LeftPadding">LeftPadding</span>
### <span id="LeftPadding">LeftPadding</span>
<p>在切片的左部添加元素。</p>
@@ -2948,10 +2949,39 @@ import (
)
func main() {
nums := []int{1, 2, 3, 4, 5}
nums := []int{1, 2, 3, 4, 5}
padded := slice.LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
```
### <span id="Frequency">Frequency</span>
<p>计算切片中每个元素出现的频率。</p>
<b>函数签名:</b>
```go
func Frequency[T comparable](slice []T) map[T]int
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "b", "c", "c", "c"}
result := slice.Frequency(strs)
fmt.Println(result)
// Output:
// map[a:1 b:2 c:3]
}
```

View File

@@ -63,6 +63,12 @@ import (
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
- [Ellipsis](#Ellipsis)
- [Shuffle](#Shuffle)
- [Rotate](#Rotate)
- [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups)
<div STYLE="page-break-after: always;"></div>
@@ -1541,7 +1547,7 @@ func main() {
func Concat(length int, str ...string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
@@ -1551,16 +1557,175 @@ import (
func main() {
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}
```
### <span id="Ellipsis">Ellipsis</span>
<p>将字符串截断到指定长度,并在末尾添加省略号。</p>
<b>函数签名:</b>
```go
func Ellipsis(str string, length int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Ellipsis("hello world", 5)
result2 := strutil.Ellipsis("你好,世界!", 2)
result3 := strutil.Ellipsis("😀😃😄😁😆", 3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// hello...
// 你好...
// 😀😃😄...
}
```
### <span id="Shuffle">Shuffle</span>
<p>打乱给定字符串中的字符顺序。</p>
<b>函数签名:</b>
```go
func Shuffle(str string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result := strutil.Shuffle("hello")
fmt.Println(result) //olelh (random order)
}
```
### <span id="Rotate">Rotate</span>
<p>按指定的字符数旋转字符串。</p>
<b>函数签名:</b>
```go
func Rotate(str string, shift int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := Rotate("Hello", 0)
result2 := Rotate("Hello", 1)
result3 := Rotate("Hello", 2)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello
// oHell
// loHel
}
```
### <span id="TemplateReplace">TemplateReplace</span>
<p>将模板字符串中的占位符替换为map中的相应值。占位符括在花括号中例如 {key}。例如模板字符串为“Hello, {name}!”map为{"name": "world"}结果将为“Hello, world!”。</p>
<b>函数签名:</b>
```go
func TemplateReplace(template string, data map[string]string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
result := strutil.TemplateReplace(template, data)
fmt.Println(result)
// Output:
// Hello, my name is Bob, I'm 20 years old.
}
```
### <span id="RegexMatchAllGroups">RegexMatchAllGroups</span>
<p>使用正则表达式匹配字符串中的所有子组并返回结果。</p>
<b>函数签名:</b>
```go
func RegexMatchAllGroups(pattern, str string) [][]string
```
<b>示例:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
str := "Emails: john.doe@example.com and jane.doe@example.com"
result := strutil.RegexMatchAllGroups(pattern, str)
fmt.Println(result[0])
fmt.Println(result[1])
// Output:
// [john.doe@example.com john.doe example com]
// [jane.doe@example.com jane.doe example com]
}
```

View File

@@ -1,6 +1,6 @@
# System
system 包含 os, runtime, shell command 相关函数。
system 包含 os, 运行time, shell command 相关函数。
<div STYLE="page-break-after: always;"></div>
@@ -31,6 +31,11 @@ import (
- [CompareOsEnv](#CompareOsEnv)
- [ExecCommand](#ExecCommand)
- [GetOsBits](#GetOsBits)
- [StartProcess](#StartProcess)
- [StopProcess](#StopProcess)
- [KillProcess](#KillProcess)
- [GetProcessInfo](#GetProcessInfo)
<div STYLE="page-break-after: always;"></div>
@@ -241,13 +246,14 @@ func main() {
### <span id="ExecCommand">ExecCommand</span>
<p>执行shell命令返回命令的stdout和stderr字符串如果出现错误则返回错误。参数`command`是一个完整的命令字符串如ls-alinuxdirwindowsping 127.0.0.1。在linux中使用/bin/bash-c执行命令在windows中使用powershell.exe执行命令。</p>
<p>执行shell命令返回命令的stdout和stderr字符串如果出现错误则返回错误。参数`command`是一个完整的命令字符串如ls-alinuxdirwindowsping 127.0.0.1。在linux中使用/bin/bash-c执行命令在windows中使用powershell.exe执行命令。
函数的第二个参数是cmd选项控制参数类型是func(*exec.Cmd)可以通过这个参数设置cmd属性。</p>
<b>函数签名:</b>
```go
type (
Option func(*exec.Cmd)
Option func(*exec.Cmd)
)
func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error)
```
@@ -262,7 +268,9 @@ import (
func main() {
// linux or mac
stdout, stderr, err := system.ExecCommand("ls")
stdout, stderr, err := system.ExecCommand("ls", func(cmd *exec.Cmd) {
cmd.Dir = "/tmp"
})
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
assert.Equal("", stderr)
@@ -305,3 +313,132 @@ func main() {
fmt.Println(osBit) // 32 or 64
}
```
### <span id="StartProcess">StartProcess</span>
<p>创建进程。</p>
<b>函数签名:</b>
```go
func StartProcess(command string, args ...string) (int, error)
```
<b>示例:<span style="float:right;display:inline-block">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "2")
if err != nil {
return
}
fmt.Println(pid)
}
```
### <span id="StopProcess">StopProcess</span>
<p>停止进程。</p>
<b>函数签名:</b>
```go
func StopProcess(pid int) error
```
<b>示例:<span style="float:right;display:inline-block">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "10")
if err != nil {
return
}
time.Sleep(1 * time.Second)
err = system.StopProcess(pid)
fmt.Println(err)
// Output:
// <nil>
}
```
### <span id="KillProcess">KillProcess</span>
<p>杀掉进程。</p>
<b>函数签名:</b>
```go
func KillProcess(pid int) error
```
<b>示例:<span style="float:right;display:inline-block">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "10")
if err != nil {
return
}
time.Sleep(1 * time.Second)
err = system.KillProcess(pid)
fmt.Println(err)
// Output:
// <nil>
}
```
### <span id="GetProcessInfo">GetProcessInfo</span>
<p>根据进程id获取进程信息。</p>
<b>函数签名:</b>
```go
func GetProcessInfo(pid int) (*ProcessInfo, error)
```
<b>示例:<span style="float:right;display:inline-block">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("ls", "-a")
if err != nil {
return
}
processInfo, err := system.GetProcessInfo(pid)
if err != nil {
return
}
fmt.Println(processInfo)
}
```

View File

@@ -32,6 +32,8 @@ import (
- [AesCfbDecrypt](#AesCfbDecrypt)
- [AesOfbEncrypt](#AesOfbEncrypt)
- [AesOfbDecrypt](#AesOfbDecrypt)
- [AesGcmEncrypt](#AesGcmEncrypt)
- [AesGcmDecrypt](#AesGcmDecrypt)
- [Base64StdEncode](#Base64StdEncode)
- [Base64StdDecode](#Base64StdDecode)
- [DesEcbEncrypt](#DesEcbEncrypt)
@@ -379,6 +381,74 @@ func main() {
}
```
### <span id="AesGcmEncrypt">AesGcmEncrypt</span>
<p>Encrypt data with key use AES GCM algorithm.</p>
<b>Signature:</b>
```go
func AesGcmEncrypt(data, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="AesGcmDecrypt">AesGcmDecrypt</span>
<p>Decrypt data with key use AES GCM algorithm.</p>
<b>Signature:</b>
```go
func AesGcmDecrypt(data, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="Base64StdEncode">Base64StdEncode</span>
<p>Encode string with base64 encoding.</p>

View File

@@ -65,6 +65,10 @@ import (
- [TimestampMilli](#TimestampMilli)
- [TimestampMicro](#TimestampMicro)
- [TimestampNano](#TimestampNano)
- [TrackFuncTime](#TrackFuncTime)
- [DaysBetween](#DaysBetween)
- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
<div STYLE="page-break-after: always;"></div>
@@ -1465,3 +1469,106 @@ func main() {
// 1690363051331788000
}
```
### <span id="TrackFuncTime">TrackFuncTime</span>
<p>Tracks function execution time.</p>
<b>Signature:</b>
```go
func TrackFuncTime(pre time.Time) func()
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
defer datetime.TrackFuncTime(time.Now())()
var n int
for i := 0; i < 5000000; i++ {
n++
}
fmt.Println(1) // Function main execution time: 1.460287ms
}
```
### <span id="DaysBetween">DaysBetween</span>
<p>Returns the number of days between two times.</p>
<b>Signature:</b>
```go
func DaysBetween(start, end time.Time) int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
result := datetime.DaysBetween(start, end)
fmt.Println(result)
// Output:
// 9
}
```
### <span id="GenerateDatetimesBetween">GenerateDatetimesBetween</span>
<p>Returns a slice of strings between two times. `layout`: the format of the datetime string.`interval`: the interval between two datetimes.</p>
<b>Signature:</b>
```go
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
layout := "2006-01-02 15:04:05"
interval := "1h"
result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval)
fmt.Println(result)
fmt.Println(err)
// Output:
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
// <nil>
}
```

View File

@@ -37,12 +37,12 @@ import (
### <span id="Comma">Comma</span>
<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>
<p>Add comma to a number value by every 3 numbers from right to left. ahead by a prefix symbol char. if value is a invalid number string like "aa", return empty string.</p>
<b>Signature:</b>
```go
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string
func Comma[T constraints.Float | constraints.Integer | string](value T, prefixSymbol string) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/eRD5k2vzUVX)</span></b>

View File

@@ -204,7 +204,7 @@ func main() {
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
@@ -752,7 +752,7 @@ func main() {
func Throttle(fn func(), interval time.Duration) func()
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main

View File

@@ -7,6 +7,9 @@ Package maputil includes some functions to manipulate map.
## Source:
- [https://github.com/duke-git/lancet/blob/main/maputil/map.go](https://github.com/duke-git/lancet/blob/main/maputil/map.go)
- [https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go](https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go)
- [https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go](https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go)
<div STYLE="page-break-after: always;"></div>
@@ -47,6 +50,24 @@ import (
- [MapToStruct](#MapToStruct)
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
- [NewOrderedMap](#NewOrderedMap)
- [OrderedMap_Set](#OrderedMap_Set)
- [OrderedMap_Get](#OrderedMap_Get)
- [OrderedMap_Front](#OrderedMap_Front)
- [OrderedMap_Back](#OrderedMap_Back)
- [OrderedMap_Delete](#OrderedMap_Delete)
- [OrderedMap_Clear](#OrderedMap_Clear)
- [OrderedMap_Len](#OrderedMap_Len)
- [OrderedMap_Keys](#OrderedMap_Keys)
- [OrderedMap_Values](#OrderedMap_Values)
- [OrderedMap_Contains](#OrderedMap_Contains)
- [OrderedMap_Range](#OrderedMap_Range)
- [OrderedMap_Elements](#OrderedMap_Elements)
- [OrderedMap_Iter](#OrderedMap_Iter)
- [OrderedMap_ReverseIter](#OrderedMap_ReverseIter)
- [OrderedMap_SortByKey](#OrderedMap_SortByKey)
- [OrderedMap_MarshalJSON](#OrderedMap_MarshalJSON)
- [OrderedMap_UnmarshalJSON](#OrderedMap_UnmarshalJSON)
- [NewConcurrentMap](#NewConcurrentMap)
- [ConcurrentMap_Get](#ConcurrentMap_Get)
- [ConcurrentMap_Set](#ConcurrentMap_Set)
@@ -56,6 +77,8 @@ import (
- [ConcurrentMap_Has](#ConcurrentMap_Has)
- [ConcurrentMap_Range](#ConcurrentMap_Range)
- [GetOrSet](#GetOrSet)
- [SortByKey](#SortByKey)
- [GetOrDefault](#GetOrDefault)
<div STYLE="page-break-after: always;"></div>
@@ -1127,10 +1150,693 @@ func main() {
fmt.Println(values1)
// Output:
// [3 2 1]
// [c b a]
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
// [yesterday today tomorrow]
// [3 2 1]
// [c b a]
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
// [yesterday today tomorrow]
}
```
### <span id="NewOrderedMap">NewOrderedMap</span>
<p>Creates a new OrderedMap.</p>
<b>Signature:</b>
```go
func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Set">OrderedMap_Set</span>
<p>Sets the given key-value pair.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Set(key K, value V)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Get">OrderedMap_Get</span>
<p>Returns the value for the given key.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Get(key K) (V, bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Delete">OrderedMap_Delete</span>
<p>Deletes the key-value pair for the given key.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Delete(key K)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Delete("b")
fmt.Println(om.Keys())
// Output:
// [a c]
}
```
### <span id="OrderedMap_Clear">OrderedMap_Clear</span>
<p>Clears the map.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Clear()
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Clear()
fmt.Println(om.Keys())
// Output:
// []
}
```
### <span id="OrderedMap_Front">OrderedMap_Front</span>
<p>Returns the first key-value pair.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Front() (struct {
Key K
Value V
}, bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
frontElement, ok := om.Front()
fmt.Println(frontElement)
fmt.Println(ok)
// Output:
// {a 1}
// true
}
```
### <span id="OrderedMap_Back">OrderedMap_Back</span>
<p>Returns the last key-value pair.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Back() (struct {
Key K
Value V
}, bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
frontElement, ok := om.Front()
fmt.Println(frontElement)
fmt.Println(ok)
// Output:
// {a 1}
// true
}
```
### <span id="OrderedMap_Range">OrderedMap_Range</span>
<p>Calls the given function for each key-value pair.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Range(func(key string, value int) bool {
fmt.Println(key, value)
return true
})
// Output:
// a 1
// b 2
// c 3
}
```
### <span id="OrderedMap_Keys">OrderedMap_Keys</span>
<p>Returns the keys in order.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Keys() []K
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
keys := om.Keys()
fmt.Println(keys)
// Output:
// [a b c]
}
```
### <span id="OrderedMap_Values">OrderedMap_Values</span>
<p>Returns the values in order.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Values() []V
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
values := om.Values()
fmt.Println(values)
// Output:
// [1 2 3]
}
```
### <span id="OrderedMap_Elements">OrderedMap_Elements</span>
<p>Returns the key-value pairs in order.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Elements() []struct
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
elements := om.Elements()
fmt.Println(elements)
// Output:
// [{a 1} {b 2} {c 3}]
}
```
### <span id="OrderedMap_Len">OrderedMap_Len</span>
<p>Returns the number of key-value pairs.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Len() int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Len()
fmt.Println(om.Len())
// Output:
// 3
}
```
### <span id="OrderedMap_Contains">OrderedMap_Contains</span>
<p>Returns true if the given key exists.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Contains(key K) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
result1 := om.Contains("a")
result2 := om.Contains("d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="OrderedMap_Iter">OrderedMap_Iter</span>
<p>Returns a channel that yields key-value pairs in order.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Iter() <-chan struct {
Key K
Value V
}
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.Iter() {
fmt.Println(elem)
}
// Output:
// {a 1}
// {b 2}
// {c 3}
}
```
### <span id="OrderedMap_ReverseIter">OrderedMap_ReverseIter</span>
<p>Returns a channel that yields key-value pairs in reverse order.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) ReverseIter() <-chan struct {
Key K
Value V
}
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.ReverseIter() {
fmt.Println(elem)
}
// Output:
// {c 3}
// {b 2}
// {a 1}
}
```
### <span id="OrderedMap_SortByKey">OrderedMap_SortByKey</span>
<p>Sorts the map by key given less function.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
om.SortByKey(func(a, b int) bool {
return a < b
})
fmt.Println(om.Elements())
// Output:
// [{1 a} {2 b} {3 c} {4 d}]
}
```
### <span id="OrderedMap_MarshalJSON">OrderedMap_MarshalJSON</span>
<p>Implements the json.Marshaler interface.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
b, _ := om.MarshalJSON()
fmt.Println(string(b))
// Output:
// {"a":1,"b":2,"c":3}
}
```
### <span id="OrderedMap_UnmarshalJSON">OrderedMap_UnmarshalJSON</span>
<p>Implements the json.Unmarshaler interface.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
data := []byte(`{"a":1,"b":2,"c":3}`)
om.UnmarshalJSON(data)
fmt.Println(om.Elements())
// Output:
}
```
@@ -1540,17 +2246,17 @@ func main() {
}
```
### <span id="SortByKeys">SortByKeys</span>
### <span id="SortByKey">SortByKey</span>
<p>Sorts the map by its keys and returns a new map with sorted keys.</p>
<b>Signature:</b>
```go
func SortByKeys[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
```
<b>Example:<span style="float:right;display:inline-block;">[运行]()</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
@@ -1568,11 +2274,51 @@ func main() {
2: "b",
}
result := maputil.SortByKeys(m)
result := maputil.SortByKey(m)
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
```
### <span id="GetOrDefault">GetOrDefault</span>
<p>returns the value of the given key or a default value if the key is not present.</p>
<b>Signature:</b>
```go
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V
```
<b>Example:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result1 := maputil.GetOrDefault(m, 1, "default")
result2 := maputil.GetOrDefault(m, 6, "default")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// a
// default
}
```

View File

@@ -133,7 +133,7 @@ func main() {
func RandFromGivenSlice[T any](slice []T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
@@ -160,7 +160,7 @@ func main() {
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
@@ -346,7 +346,7 @@ func main() {
func RandIntSlice(length, min, max int) []int
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
@@ -452,7 +452,7 @@ func main() {
func RandStringSlice(charset string, sliceLen, strLen int) []string
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
@@ -481,7 +481,7 @@ func main() {
func RandBool() bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
@@ -507,7 +507,7 @@ func main() {
func RandBoolSlice(length int) []bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main

View File

@@ -105,6 +105,7 @@ import (
- [Break](#Break)
- [RightPadding](#RightPadding)
- [LeftPadding](#LeftPadding)
- [Frequency](#Frequency)
<div STYLE="page-break-after: always;"></div>
@@ -912,7 +913,7 @@ func main() {
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
@@ -1189,7 +1190,7 @@ func main() {
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int)
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
@@ -1765,7 +1766,7 @@ func main() {
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T
```
<b>Example:<span style="float:right;display:inline-block;">[运行]()</span></b>
<b>Example:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
@@ -2897,7 +2898,7 @@ func main() {
}
```
<span id="c">RightPadding</span>
### <span id="RightPadding">RightPadding</span>
<p>RightPadding adds padding to the right end of a slice.</p>
@@ -2916,7 +2917,7 @@ import (
)
func main() {
nums := []int{1, 2, 3, 4, 5}
nums := []int{1, 2, 3, 4, 5}
padded := slice.RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
@@ -2924,7 +2925,7 @@ func main() {
}
```
<span id="LeftPadding">LeftPadding</span>
### <span id="LeftPadding">LeftPadding</span>
<p>LeftPadding adds padding to the left begin of a slice.</p>
@@ -2943,10 +2944,39 @@ import (
)
func main() {
nums := []int{1, 2, 3, 4, 5}
nums := []int{1, 2, 3, 4, 5}
padded := slice.LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
```
### <span id="Frequency">Frequency</span>
<p>Counts the frequency of each element in the slice.</p>
<b>Signature:</b>
```go
func Frequency[T comparable](slice []T) map[T]int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "b", "c", "c", "c"}
result := slice.Frequency(strs)
fmt.Println(result)
// Output:
// map[a:1 b:2 c:3]
}
```

View File

@@ -63,6 +63,11 @@ import (
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
- [Ellipsis](#Ellipsis)
- [Shuffle](#Shuffle)
- [Rotate](#Rotate)
- [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups)
<div STYLE="page-break-after: always;"></div>
@@ -1100,10 +1105,10 @@ import (
func main() {
result1 := strutil.IsNotBlank("")
result2 := strutil.IsNotBlank(" ")
result2 := strutil.IsNotBlank(" ")
result3 := strutil.IsNotBlank("\t\v\f\n")
result4 := strutil.IsNotBlank(" 中文")
result5 := strutil.IsNotBlank(" world ")
result5 := strutil.IsNotBlank(" world ")
fmt.Println(result1)
fmt.Println(result2)
@@ -1544,7 +1549,7 @@ func main() {
func Concat(length int, str ...string) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
@@ -1553,17 +1558,174 @@ import (
)
func main() {
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}
```
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
### <span id="Ellipsis">Ellipsis</span>
<p>Truncates a string to a specified length and appends an ellipsis.</p>
<b>Signature:</b>
```go
func Ellipsis(str string, length int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Ellipsis("hello world", 5)
result2 := strutil.Ellipsis("你好,世界!", 2)
result3 := strutil.Ellipsis("😀😃😄😁😆", 3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// hello...
// 你好...
// 😀😃😄...
}
```
### <span id="Shuffle">Shuffle</span>
<p>Shuffle the order of characters of given string.</p>
<b>Signature:</b>
```go
func Shuffle(str string) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result := strutil.Shuffle("hello")
fmt.Println(result) //olelh (random order)
}
```
### <span id="Rotate">Rotate</span>
<p>Rotates the string by the specified number of characters.</p>
<b>Signature:</b>
```go
func Rotate(str string, shift int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := Rotate("Hello", 0)
result2 := Rotate("Hello", 1)
result3 := Rotate("Hello", 2)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello
// oHell
// loHel
}
```
### <span id="TemplateReplace">TemplateReplace</span>
<p>Replaces the placeholders in the template string with the corresponding values in the data map.The placeholders are enclosed in curly braces, e.g. {key}. for example, the template string is "Hello, {name}!", and the data map is {"name": "world"}, the result will be "Hello, world!".</p>
<b>Signature:</b>
```go
func TemplateReplace(template string, data map[string]string string
```
<b>example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
result := strutil.TemplateReplace(template, data)
fmt.Println(result)
// Output:
// Hello, my name is Bob, I'm 20 years old.
}
```
### <span id="RegexMatchAllGroups">RegexMatchAllGroups</span>
<p>Matches all subgroups in a string using a regular expression and returns the result.</p>
<b>Signature:</b>
```go
func RegexMatchAllGroups(pattern, str string) [][]string
```
<b>example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
str := "Emails: john.doe@example.com and jane.doe@example.com"
result := strutil.RegexMatchAllGroups(pattern, str)
fmt.Println(result[0])
fmt.Println(result[1])
// Output:
// [john.doe@example.com john.doe example com]
// [jane.doe@example.com jane.doe example com]
}
```

View File

@@ -31,6 +31,11 @@ import (
- [CompareOsEnv](#CompareOsEnv)
- [ExecCommand](#ExecCommand)
- [GetOsBits](#GetOsBits)
- [StartProcess](#StartProcess)
- [StopProcess](#StopProcess)
- [KillProcess](#KillProcess)
- [GetProcessInfo](#GetProcessInfo)
<div STYLE="page-break-after: always;"></div>
@@ -242,13 +247,14 @@ func main() {
### <span id="ExecCommand">ExecCommand</span>
<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>
<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.
The second parameter of the function is the cmd option control parameter. The type is func(*exec.Cmd). You can set the cmd attribute through this parameter.</p>
<b>Signature:</b>
```go
type (
Option func(*exec.Cmd)
Option func(*exec.Cmd)
)
func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error)
```
@@ -263,7 +269,9 @@ import (
func main() {
// linux or mac
stdout, stderr, err := system.ExecCommand("ls")
stdout, stderr, err := system.ExecCommand("ls", func(cmd *exec.Cmd) {
cmd.Dir = "/tmp"
})
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
assert.Equal("", stderr)
@@ -285,7 +293,7 @@ func main() {
### <span id="GetOsBits">GetOsBits</span>
<p>Get current os bits, 32bit or 64bit. return 32 or 64</p>
<p>Get current os bits, 32bit or 64bit. return 32 or 64.</p>
<b>Signature:</b>
@@ -306,3 +314,132 @@ func main() {
fmt.Println(osBit) // 32 or 64
}
```
### <span id="StartProcess">StartProcess</span>
<p>Start a new process with the specified name and arguments.</p>
<b>Signature:</b>
```go
func StartProcess(command string, args ...string) (int, error)
```
<b>Example:<span style="float:right;display:inline-block">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "2")
if err != nil {
return
}
fmt.Println(pid)
}
```
### <span id="StopProcess">StopProcess</span>
<p>Stop a process by pid.</p>
<b>Signature:</b>
```go
func StopProcess(pid int) error
```
<b>Example:<span style="float:right;display:inline-block">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "10")
if err != nil {
return
}
time.Sleep(1 * time.Second)
err = system.StopProcess(pid)
fmt.Println(err)
// Output:
// <nil>
}
```
### <span id="KillProcess">KillProcess</span>
<p>Kill a process by pid.</p>
<b>Signature:</b>
```go
func KillProcess(pid int) error
```
<b>Example:<span style="float:right;display:inline-block">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "10")
if err != nil {
return
}
time.Sleep(1 * time.Second)
err = system.KillProcess(pid)
fmt.Println(err)
// Output:
// <nil>
}
```
### <span id="GetProcessInfo">GetProcessInfo</span>
<p>Retrieves detailed process information by pid.</p>
<b>Signature:</b>
```go
func GetProcessInfo(pid int) (*ProcessInfo, error)
```
<b>Example:<span style="float:right;display:inline-block">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("ls", "-a")
if err != nil {
return
}
processInfo, err := system.GetProcessInfo(pid)
if err != nil {
return
}
fmt.Println(processInfo)
}
```

View File

@@ -18,63 +18,63 @@ import (
// http://en.wikipedia.org/wiki/Binary_prefix
const (
// Decimal
UnitB = 1
UnitKB = 1000
UnitMB = 1000 * UnitKB
UnitGB = 1000 * UnitMB
UnitTB = 1000 * UnitGB
UnitPB = 1000 * UnitTB
UnitEB = 1000 * UnitPB
unitB = 1
unitKB = 1000
unitMB = 1000 * unitKB
unitGB = 1000 * unitMB
unitTB = 1000 * unitGB
unitPB = 1000 * unitTB
unitEB = 1000 * unitPB
// Binary
UnitBiB = 1
UnitKiB = 1024
UnitMiB = 1024 * UnitKiB
UnitGiB = 1024 * UnitMiB
UnitTiB = 1024 * UnitGiB
UnitPiB = 1024 * UnitTiB
UnitEiB = 1024 * UnitPiB
unitBiB = 1
unitKiB = 1024
unitMiB = 1024 * unitKiB
unitGiB = 1024 * unitMiB
unitTiB = 1024 * unitGiB
unitPiB = 1024 * unitTiB
unitEiB = 1024 * unitPiB
)
// type byteUnitMap map[byte]int64
var (
decimalByteMap = map[string]uint64{
"b": UnitB,
"kb": UnitKB,
"mb": UnitMB,
"gb": UnitGB,
"tb": UnitTB,
"pb": UnitPB,
"eb": UnitEB,
"b": unitB,
"kb": unitKB,
"mb": unitMB,
"gb": unitGB,
"tb": unitTB,
"pb": unitPB,
"eb": unitEB,
// Without suffix
"": UnitB,
"k": UnitKB,
"m": UnitMB,
"g": UnitGB,
"t": UnitTB,
"p": UnitPB,
"e": UnitEB,
"": unitB,
"k": unitKB,
"m": unitMB,
"g": unitGB,
"t": unitTB,
"p": unitPB,
"e": unitEB,
}
binaryByteMap = map[string]uint64{
"bi": UnitBiB,
"kib": UnitKiB,
"mib": UnitMiB,
"gib": UnitGiB,
"tib": UnitTiB,
"pib": UnitPiB,
"eib": UnitEiB,
"bi": unitBiB,
"kib": unitKiB,
"mib": unitMiB,
"gib": unitGiB,
"tib": unitTiB,
"pib": unitPiB,
"eib": unitEiB,
// Without suffix
"": UnitBiB,
"ki": UnitKiB,
"mi": UnitMiB,
"gi": UnitGiB,
"ti": UnitTiB,
"pi": UnitPiB,
"ei": UnitEiB,
"": unitBiB,
"ki": unitKiB,
"mi": unitMiB,
"gi": unitGiB,
"ti": unitTiB,
"pi": unitPiB,
"ei": unitEiB,
}
)

View File

@@ -15,11 +15,11 @@ func TestDecimalBytes(t *testing.T) {
assert.Equal("1.024KB", DecimalBytes(1024))
assert.Equal("1.2346MB", DecimalBytes(1234567))
assert.Equal("1.235MB", DecimalBytes(1234567, 3))
assert.Equal("1.123GB", DecimalBytes(float64(1.123*UnitGB)))
assert.Equal("2.123TB", DecimalBytes(float64(2.123*UnitTB)))
assert.Equal("3.123PB", DecimalBytes(float64(3.123*UnitPB)))
assert.Equal("4.123EB", DecimalBytes(float64(4.123*UnitEB)))
assert.Equal("1EB", DecimalBytes(float64(1000*UnitPB)))
assert.Equal("1.123GB", DecimalBytes(float64(1.123*unitGB)))
assert.Equal("2.123TB", DecimalBytes(float64(2.123*unitTB)))
assert.Equal("3.123PB", DecimalBytes(float64(3.123*unitPB)))
assert.Equal("4.123EB", DecimalBytes(float64(4.123*unitEB)))
assert.Equal("1EB", DecimalBytes(float64(1000*unitPB)))
assert.Equal("62MB", DecimalBytes(61812496, 0))
assert.Equal("61.8MB", DecimalBytes(61812496, 1))
assert.Equal("401MB", DecimalBytes(401000000))

View File

@@ -14,11 +14,11 @@ import (
"golang.org/x/exp/constraints"
)
// Comma add comma to a number value by every 3 numbers from right. ahead by symbol char.
// Comma add comma to a number value by every 3 numbers from right. ahead by prefix 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 {
func Comma[T constraints.Float | constraints.Integer | string](value T, prefixSymbol string) string {
numString := convertor.ToString(value)
_, err := strconv.ParseFloat(numString, 64)
@@ -26,17 +26,26 @@ func Comma[T constraints.Float | constraints.Integer | string](value T, symbol s
return ""
}
isNegative := strings.HasPrefix(numString, "-")
if isNegative {
numString = numString[1:]
}
index := strings.Index(numString, ".")
if index == -1 {
index = len(numString)
}
for index > 3 {
index = index - 3
index -= 3
numString = numString[:index] + "," + numString[index:]
}
return symbol + numString
if isNegative {
numString = "-" + numString
}
return prefixSymbol + numString
}
// Pretty data to JSON string.

View File

@@ -28,6 +28,10 @@ func TestComma(t *testing.T) {
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, ""))
assert.Equal("-999", Comma(-999, ""))
assert.Equal("-1,000", Comma(-1000, ""))
assert.Equal("-1,234,567", Comma(-1234567, ""))
}
func TestPretty(t *testing.T) {

View File

@@ -454,16 +454,16 @@ func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V {
return value
}
// SortByKeys sorts the map by its keys and returns a new map with sorted keys.
// SortByKey sorts the map by its keys and returns a new map with sorted keys.
// Play: todo
func SortByKeys[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V) {
func SortByKey[K constraints.Ordered, V any](m map[K]V, less func(a, b K) bool) (sortedKeysMap map[K]V) {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return keys[i] < keys[j]
return less(keys[i], keys[j])
})
sortedKeysMap = make(map[K]V, len(m))
@@ -473,3 +473,196 @@ func SortByKeys[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
return
}
var mapHandlers = map[reflect.Kind]func(reflect.Value, reflect.Value) error{
reflect.String: convertNormal,
reflect.Int: convertNormal,
reflect.Int16: convertNormal,
reflect.Int32: convertNormal,
reflect.Int64: convertNormal,
reflect.Uint: convertNormal,
reflect.Uint16: convertNormal,
reflect.Uint32: convertNormal,
reflect.Uint64: convertNormal,
reflect.Float32: convertNormal,
reflect.Float64: convertNormal,
reflect.Uint8: convertNormal,
reflect.Int8: convertNormal,
reflect.Struct: convertNormal,
reflect.Complex64: convertNormal,
reflect.Complex128: convertNormal,
}
var _ = func() struct{} {
mapHandlers[reflect.Map] = convertMap
mapHandlers[reflect.Array] = convertSlice
mapHandlers[reflect.Slice] = convertSlice
return struct{}{}
}()
// MapTo try to map any interface to struct or base type
/*
Eg:
v := map[string]interface{}{
"service":map[string]interface{}{
"ip":"127.0.0.1",
"port":1234,
},
version:"v1.0.01"
}
type Target struct {
Service struct {
Ip string `json:"ip"`
Port int `json:"port"`
} `json:"service"`
Ver string `json:"version"`
}
var dist Target
if err := maputil.MapTo(v,&dist); err != nil {
log.Println(err)
return
}
log.Println(dist)
*/
// Play: https://go.dev/play/p/4K7KBEPgS5M
func MapTo(src any, dst any) error {
dstRef := reflect.ValueOf(dst)
if dstRef.Kind() != reflect.Ptr {
return fmt.Errorf("dst is not ptr")
}
dstElem := dstRef.Type().Elem()
if dstElem.Kind() == reflect.Struct {
srcMap := src.(map[string]interface{})
return MapToStruct(srcMap, dst)
}
dstRef = reflect.Indirect(dstRef)
srcRef := reflect.ValueOf(src)
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
srcRef = srcRef.Elem()
}
if f, ok := mapHandlers[srcRef.Kind()]; ok {
return f(srcRef, dstRef)
}
return fmt.Errorf("no implemented:%s", srcRef.Type())
}
func convertNormal(src reflect.Value, dst reflect.Value) error {
if dst.CanSet() {
if src.Type() == dst.Type() {
dst.Set(src)
} else if src.CanConvert(dst.Type()) {
dst.Set(src.Convert(dst.Type()))
} else {
return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String())
}
}
return nil
}
func convertSlice(src reflect.Value, dst reflect.Value) error {
if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice {
return fmt.Errorf("error type:%s", dst.Type().String())
}
l := src.Len()
target := reflect.MakeSlice(dst.Type(), l, l)
if dst.CanSet() {
dst.Set(target)
}
for i := 0; i < l; i++ {
srcValue := src.Index(i)
if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface {
srcValue = srcValue.Elem()
}
if f, ok := mapHandlers[srcValue.Kind()]; ok {
err := f(srcValue, dst.Index(i))
if err != nil {
return err
}
}
}
return nil
}
func convertMap(src reflect.Value, dst reflect.Value) error {
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
if src.Kind() == reflect.Interface && dst.IsValid() {
return convertMap(src.Elem(), dst)
} else {
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
}
}
dstType := dst.Type()
num := dstType.NumField()
exist := map[string]int{}
for i := 0; i < num; i++ {
k := dstType.Field(i).Tag.Get("json")
if k == "" {
k = dstType.Field(i).Name
}
if strings.Contains(k, ",") {
taglist := strings.Split(k, ",")
if taglist[0] == "" {
k = dstType.Field(i).Name
} else {
k = taglist[0]
}
}
exist[k] = i
}
keys := src.MapKeys()
for _, key := range keys {
if index, ok := exist[key.String()]; ok {
v := dst.Field(index)
if v.Kind() == reflect.Struct {
err := convertMap(src.MapIndex(key), v)
if err != nil {
return err
}
} else {
if v.CanSet() {
if v.Type() == src.MapIndex(key).Elem().Type() {
v.Set(src.MapIndex(key).Elem())
} else if src.MapIndex(key).Elem().CanConvert(v.Type()) {
v.Set(src.MapIndex(key).Elem().Convert(v.Type()))
} else if f, ok := mapHandlers[src.MapIndex(key).Elem().Kind()]; ok && f != nil {
err := f(src.MapIndex(key).Elem(), v)
if err != nil {
return err
}
} else {
return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type())
}
}
}
}
}
return nil
}
// GetOrDefault returns the value of the given key or a default value if the key is not present.
// Play: todo
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V {
if v, ok := m[key]; ok {
return v
}
return defaultValue
}

View File

@@ -541,7 +541,7 @@ func ExampleGetOrSet() {
// b
}
func ExampleSortByKeys() {
func ExampleSortByKey() {
m := map[int]string{
3: "c",
1: "a",
@@ -549,10 +549,283 @@ func ExampleSortByKeys() {
2: "b",
}
result := SortByKeys(m)
result := SortByKey(m, func(a, b int) bool {
return a < b
})
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
func ExampleOrderedMap_Set() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
func ExampleOrderedMap_Get() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
func ExampleOrderedMap_Front() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
frontElement, ok := om.Front()
fmt.Println(frontElement)
fmt.Println(ok)
// Output:
// {a 1}
// true
}
func ExampleOrderedMap_Back() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
backElement, ok := om.Back()
fmt.Println(backElement)
fmt.Println(ok)
// Output:
// {c 3}
// true
}
func ExampleOrderedMap_Delete() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Delete("b")
fmt.Println(om.Keys())
// Output:
// [a c]
}
func ExampleOrderedMap_Clear() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Clear()
fmt.Println(om.Keys())
// Output:
// []
}
func ExampleOrderedMap_Len() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Len()
fmt.Println(om.Len())
// Output:
// 3
}
func ExampleOrderedMap_Keys() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
keys := om.Keys()
fmt.Println(keys)
// Output:
// [a b c]
}
func ExampleOrderedMap_Values() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
values := om.Values()
fmt.Println(values)
// Output:
// [1 2 3]
}
func ExampleOrderedMap_Contains() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
result1 := om.Contains("a")
result2 := om.Contains("d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
func ExampleOrderedMap_Range() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Range(func(key string, value int) bool {
fmt.Println(key, value)
return true
})
// Output:
// a 1
// b 2
// c 3
}
func ExampleOrderedMap_Elements() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
elements := om.Elements()
fmt.Println(elements)
// Output:
// [{a 1} {b 2} {c 3}]
}
func ExampleOrderedMap_Iter() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.Iter() {
fmt.Println(elem)
}
// Output:
// {a 1}
// {b 2}
// {c 3}
}
func ExampleOrderedMap_ReverseIter() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.ReverseIter() {
fmt.Println(elem)
}
// Output:
// {c 3}
// {b 2}
// {a 1}
}
func ExampleOrderedMap_SortByKey() {
om := NewOrderedMap[int, string]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
om.SortByKey(func(a, b int) bool {
return a < b
})
fmt.Println(om.Elements())
// Output:
// [{1 a} {2 b} {3 c} {4 d}]
}
func ExampleOrderedMap_MarshalJSON() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
b, _ := om.MarshalJSON()
fmt.Println(string(b))
// Output:
// {"a":1,"b":2,"c":3}
}
func ExampleOrderedMap_UnmarshalJSON() {
// om := NewOrderedMap[string, int]()
// data := []byte(`{"a":1,"b":2,"c":3}`)
// om.UnmarshalJSON(data)
// fmt.Println(om.Elements())
}

View File

@@ -708,10 +708,10 @@ func TestGetOrSet(t *testing.T) {
assert.Equal("b", result2)
}
func TestSortByKeys(t *testing.T) {
func TestSortByKey(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSortByKeys")
assert := internal.NewAssert(t, "TestSortByKey")
m1 := map[int]string{
3: "c",
@@ -726,7 +726,9 @@ func TestSortByKeys(t *testing.T) {
4: "d",
}
result1 := SortByKeys(m1)
result1 := SortByKey(m1, func(a, b int) bool {
return a < b
})
assert.Equal(expected1, result1)
@@ -737,13 +739,152 @@ func TestSortByKeys(t *testing.T) {
"b": 2,
}
expected2 := map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 4,
"c": 3,
"b": 2,
"a": 1,
}
result2 := SortByKeys(m2)
result2 := SortByKey(m2, func(a, b string) bool {
return a > b
})
assert.Equal(expected2, result2)
}
type (
Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Address *Address `json:"address"`
}
Address struct {
Street string `json:"street"`
Number int `json:"number"`
}
)
func TestStructType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestStructType")
src := map[string]interface{}{
"name": "Nothin",
"age": 28,
"phone": "123456789",
"address": map[string]interface{}{
"street": "test",
"number": 1,
},
}
var p Person
err := MapTo(src, &p)
assert.IsNil(err)
assert.Equal(src["name"], p.Name)
assert.Equal(src["age"], p.Age)
assert.Equal(src["phone"], p.Phone)
assert.Equal("test", p.Address.Street)
assert.Equal(1, p.Address.Number)
}
func TestBaseType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBaseType")
tc := map[string]interface{}{
"i32": -32,
"i8": -8,
"i16": -16,
"i64": -64,
"i": -1,
"u32": 32,
"u8": 8,
"u16": 16,
"u64": 64,
"u": 1,
"tf": true,
"f32": 1.32,
"f64": 1.64,
"str": "hello mapto",
"complex": 1 + 3i,
}
type BaseType struct {
I int `json:"i"`
I8 int8 `json:"i8"`
I16 int16 `json:"i16"`
I32 int32 `json:"i32"`
I64 int64 `json:"i64"`
U uint `json:"u"`
U8 uint8 `json:"u8"`
U16 uint16 `json:"u16"`
U32 uint32 `json:"u32"`
U64 uint64 `json:"u64"`
F32 float32 `json:"f32"`
F64 float64 `json:"f64"`
Tf bool `json:"tf"`
Str string `json:"str"`
Comp complex128 `json:"complex"`
}
var dist BaseType
err := MapTo(tc, &dist)
assert.IsNil(err)
var number float64
MapTo(tc["i"], &number)
assert.EqualValues(-1, number)
MapTo(tc["i8"], &number)
assert.EqualValues(-8, number)
MapTo(tc["i16"], &number)
assert.EqualValues(-16, number)
MapTo(tc["i32"], &number)
assert.EqualValues(-32, number)
MapTo(tc["i64"], &number)
assert.EqualValues(-64, number)
MapTo(tc["u"], &number)
assert.EqualValues(1, number)
MapTo(tc["u8"], &number)
assert.EqualValues(8, number)
MapTo(tc["u16"], &number)
assert.EqualValues(16, number)
MapTo(tc["u32"], &number)
assert.EqualValues(32, number)
MapTo(tc["u64"], &number)
assert.EqualValues(64, number)
}
func TestGetOrDefault(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "GetOrDefault")
m1 := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result1 := GetOrDefault(m1, 1, "123")
assert.Equal("a", result1)
result2 := GetOrDefault(m1, 5, "123")
assert.Equal("123", result2)
}

431
maputil/orderedmap.go Normal file
View File

@@ -0,0 +1,431 @@
package maputil
import (
"container/list"
"encoding/json"
"fmt"
"reflect"
"sort"
"strconv"
"sync"
)
// OrderedMap is a map that maintains the order of keys.
type OrderedMap[K comparable, V any] struct {
mu sync.RWMutex
data map[K]V
order *list.List
index map[K]*list.Element
}
// NewOrderedMap creates a new OrderedMap.
func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V] {
return &OrderedMap[K, V]{
data: make(map[K]V),
order: list.New(),
index: make(map[K]*list.Element),
}
}
// Sets the given key-value pair.
// Play: todo
func (om *OrderedMap[K, V]) Set(key K, value V) {
om.mu.Lock()
defer om.mu.Unlock()
if elem, ok := om.index[key]; ok {
elem.Value = value
om.order.MoveToBack(elem)
return
}
om.data[key] = value
elem := om.order.PushBack(key)
om.index[key] = elem
}
// Get returns the value for the given key.
// Play: todo
func (om *OrderedMap[K, V]) Get(key K) (V, bool) {
om.mu.RLock()
defer om.mu.RUnlock()
value, ok := om.data[key]
return value, ok
}
// Delete deletes the given key.
// Play: todo
func (om *OrderedMap[K, V]) Delete(key K) {
om.mu.Lock()
defer om.mu.Unlock()
if elem, ok := om.index[key]; ok {
om.order.Remove(elem)
delete(om.data, key)
delete(om.index, key)
}
}
// Clear clears the map.
// Play: todo
func (om *OrderedMap[K, V]) Clear() {
om.mu.Lock()
defer om.mu.Unlock()
om.data = make(map[K]V)
om.order.Init()
om.index = make(map[K]*list.Element)
}
// Front returns the first key-value pair.
// Play: todo
func (om *OrderedMap[K, V]) Front() (struct {
Key K
Value V
}, bool) {
om.mu.RLock()
defer om.mu.RUnlock()
if elem := om.order.Front(); elem != nil {
key := elem.Value.(K)
value := om.data[key]
return struct {
Key K
Value V
}{
Key: key,
Value: value,
}, true
}
return struct {
Key K
Value V
}{}, false
}
// Back returns the last key-value pair.
// Play: todo
func (om *OrderedMap[K, V]) Back() (struct {
Key K
Value V
}, bool) {
om.mu.RLock()
defer om.mu.RUnlock()
if elem := om.order.Back(); elem != nil {
key := elem.Value.(K)
value := om.data[key]
return struct {
Key K
Value V
}{
Key: key,
Value: value,
}, true
}
return struct {
Key K
Value V
}{}, false
}
// Range calls the given function for each key-value pair.
// Play: todo
func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool) {
om.mu.RLock()
defer om.mu.RUnlock()
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
value := om.data[key]
if !iteratee(key, value) {
break
}
}
}
// Keys returns the keys in order.
// Play: todo
func (om *OrderedMap[K, V]) Keys() []K {
om.mu.RLock()
defer om.mu.RUnlock()
keys := make([]K, 0, len(om.data))
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
keys = append(keys, elem.Value.(K))
}
return keys
}
// Values returns the values in order.
// Play: todo
func (om *OrderedMap[K, V]) Values() []V {
om.mu.RLock()
defer om.mu.RUnlock()
values := make([]V, 0, len(om.data))
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
values = append(values, om.data[key])
}
return values
}
// Len returns the number of key-value pairs.
// Play: todo
func (om *OrderedMap[K, V]) Len() int {
om.mu.RLock()
defer om.mu.RUnlock()
return len(om.data)
}
// Contains returns true if the given key exists.
// Play: todo
func (om *OrderedMap[K, V]) Contains(key K) bool {
om.mu.RLock()
defer om.mu.RUnlock()
_, ok := om.data[key]
return ok
}
// Elements returns the key-value pairs in order.
// Play: todo
func (om *OrderedMap[K, V]) Elements() []struct {
Key K
Value V
} {
om.mu.RLock()
defer om.mu.RUnlock()
elements := make([]struct {
Key K
Value V
}, 0, len(om.data))
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
value := om.data[key]
elements = append(elements, struct {
Key K
Value V
}{Key: key, Value: value})
}
return elements
}
// Iter returns a channel that yields key-value pairs in order.
// Play: todo
func (om *OrderedMap[K, V]) Iter() <-chan struct {
Key K
Value V
} {
ch := make(chan struct {
Key K
Value V
})
go func() {
om.mu.RLock()
defer om.mu.RUnlock()
defer close(ch)
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
value := om.data[key]
ch <- struct {
Key K
Value V
}{Key: key, Value: value}
}
}()
return ch
}
// ReverseIter returns a channel that yields key-value pairs in reverse order.
// Play: todo
func (om *OrderedMap[K, V]) ReverseIter() <-chan struct {
Key K
Value V
} {
ch := make(chan struct {
Key K
Value V
})
go func() {
om.mu.RLock()
defer om.mu.RUnlock()
defer close(ch)
for elem := om.order.Back(); elem != nil; elem = elem.Prev() {
key := elem.Value.(K)
value := om.data[key]
ch <- struct {
Key K
Value V
}{Key: key, Value: value}
}
}()
return ch
}
// SortByValue sorts the map by key given less function.
// Play: todo
func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool) {
om.mu.Lock()
defer om.mu.Unlock()
keys := make([]K, 0, om.order.Len())
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
keys = append(keys, elem.Value.(K))
}
sort.Slice(keys, func(i, j int) bool {
return less(keys[i], keys[j])
})
om.order.Init()
om.index = make(map[K]*list.Element)
for _, key := range keys {
elem := om.order.PushBack(key)
om.index[key] = elem
}
}
// MarshalJSON implements the json.Marshaler interface.
// Play: todo
func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) {
om.mu.RLock()
defer om.mu.RUnlock()
tempMap := make(map[string]V)
for e := om.order.Front(); e != nil; e = e.Next() {
key := e.Value.(K)
keyStr, err := keyToString(key)
if err != nil {
return nil, err
}
tempMap[keyStr] = om.data[key]
}
return json.Marshal(tempMap)
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// Play: todo
func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error {
om.mu.Lock()
defer om.mu.Unlock()
tempMap := make(map[string]V)
if err := json.Unmarshal(data, &tempMap); err != nil {
return err
}
om.data = make(map[K]V)
om.order.Init()
om.index = make(map[K]*list.Element)
for keyStr, value := range tempMap {
key, err := stringToKey[K](keyStr)
if err != nil {
return err
}
om.data[key] = value
elem := om.order.PushBack(key)
om.index[key] = elem
}
return nil
}
func keyToString[K any](key K) (string, error) {
switch v := any(key).(type) {
case int:
return strconv.Itoa(v), nil
case float64:
return strconv.FormatFloat(v, 'f', -1, 64), nil
case string:
return v, nil
default:
// 使用反射将未知类型转换为字符串
rv := reflect.ValueOf(key)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10), nil
case reflect.Float32, reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'f', -1, 64), nil
case reflect.String:
return rv.String(), nil
default:
return "", fmt.Errorf("unsupported key type: %T", key)
}
}
}
func stringToKey[K any](s string) (K, error) {
var zero K
switch any(zero).(type) {
case int:
value, err := strconv.Atoi(s)
return any(value).(K), err
case float64:
value, err := strconv.ParseFloat(s, 64)
return any(value).(K), err
case string:
return any(s).(K), nil
default:
// 使用反射恢复未知类型的键
rv := reflect.ValueOf(&zero).Elem()
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return zero, err
}
rv.SetInt(val)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return zero, err
}
rv.SetUint(val)
case reflect.Float32, reflect.Float64:
val, err := strconv.ParseFloat(s, 64)
if err != nil {
return zero, err
}
rv.SetFloat(val)
case reflect.String:
rv.SetString(s)
default:
return zero, fmt.Errorf("unsupported key type: %T", zero)
}
return rv.Interface().(K), nil
}
}

245
maputil/orderedmap_test.go Normal file
View File

@@ -0,0 +1,245 @@
package maputil
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestOrderedMap_Set_Get(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Set_Get")
val, ok := om.Get("a")
assert.Equal(1, val)
assert.Equal(true, ok)
val, ok = om.Get("d")
assert.Equal(false, ok)
assert.Equal(0, val)
}
func TestOrderedMap_Front_Back(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Front_Back")
frontElement, ok := om.Front()
assert.Equal("a", frontElement.Key)
assert.Equal(1, frontElement.Value)
assert.Equal(true, ok)
backElement, ok := om.Back()
assert.Equal("c", backElement.Key)
assert.Equal(3, backElement.Value)
assert.Equal(true, ok)
}
func TestOrderedMap_Delete_Clear(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Delete_Clear")
assert.Equal(3, om.Len())
om.Delete("b")
assert.Equal(2, om.Len())
om.Clear()
assert.Equal(0, om.Len())
}
func TestOrderedMap_Keys_Values(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Keys_Values")
assert.Equal([]string{"a", "b", "c"}, om.Keys())
assert.Equal([]int{1, 2, 3}, om.Values())
}
func TestOrderedMap_Contains(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Contains")
assert.Equal(true, om.Contains("a"))
assert.Equal(false, om.Contains("d"))
}
func TestOrderedMap_Eelements(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Eelements")
elements := []struct {
Key string
Value int
}{
{"a", 1},
{"b", 2},
{"c", 3},
}
assert.Equal(elements, om.Elements())
}
func TestOrderedMap_Range(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Set("d", 4)
assert := internal.NewAssert(t, "TestOrderedMap_Range")
var keys []string
om.Range(func(key string, value int) bool {
keys = append(keys, key)
return key != "c"
})
assert.Equal([]string{"a", "b", "c"}, keys)
}
func TestOrderedMap_Iter(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Iter")
var items []struct {
Key string
Value int
}
iterCh := om.Iter()
for item := range iterCh {
items = append(items, item)
}
expected := []struct {
Key string
Value int
}{
{"a", 1},
{"b", 2},
{"c", 3},
}
assert.Equal(expected, items)
}
func TestOrderedMap_ReverseIter(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_ReverseIter")
var items []struct {
Key string
Value int
}
iterCh := om.ReverseIter()
for item := range iterCh {
items = append(items, item)
}
expected := []struct {
Key string
Value int
}{
{"c", 3},
{"b", 2},
{"a", 1},
}
assert.Equal(expected, items)
}
func TestOrderedMap_SortByKey(t *testing.T) {
assert := internal.NewAssert(t, "TestOrderedMap_SortByKey")
om := NewOrderedMap[string, int]()
om.Set("d", 4)
om.Set("b", 2)
om.Set("c", 3)
om.Set("a", 1)
om.SortByKey(func(a, b string) bool {
return a < b
})
assert.Equal([]string{"a", "b", "c", "d"}, om.Keys())
}
func TestOrderedMap_MarshalJSON(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_MarshalJSON")
jsonBytes, err := om.MarshalJSON()
if err != nil {
t.Errorf("MarshalJSON error: %v", err)
}
assert.Equal(`{"a":1,"b":2,"c":3}`, string(jsonBytes))
}
func TestOrderedMap_UnmarshalJSON(t *testing.T) {
assert := internal.NewAssert(t, "TestOrderedMap_UnmarshalJSON")
jsonStr := `{"a":1,"b":2,"c":3}`
om := NewOrderedMap[string, int]()
err := om.UnmarshalJSON([]byte(jsonStr))
if err != nil {
t.Errorf("MarshalJSON error: %v", err)
}
assert.Equal(3, om.Len())
assert.Equal(true, om.Contains("a"))
assert.Equal(true, om.Contains("b"))
assert.Equal(true, om.Contains("c"))
}

View File

@@ -1,191 +0,0 @@
package maputil
import (
"fmt"
"reflect"
"strings"
)
var mapHandlers = map[reflect.Kind]func(reflect.Value, reflect.Value) error{
reflect.String: convertNormal,
reflect.Int: convertNormal,
reflect.Int16: convertNormal,
reflect.Int32: convertNormal,
reflect.Int64: convertNormal,
reflect.Uint: convertNormal,
reflect.Uint16: convertNormal,
reflect.Uint32: convertNormal,
reflect.Uint64: convertNormal,
reflect.Float32: convertNormal,
reflect.Float64: convertNormal,
reflect.Uint8: convertNormal,
reflect.Int8: convertNormal,
reflect.Struct: convertNormal,
reflect.Complex64: convertNormal,
reflect.Complex128: convertNormal,
}
var _ = func() struct{} {
mapHandlers[reflect.Map] = convertMap
mapHandlers[reflect.Array] = convertSlice
mapHandlers[reflect.Slice] = convertSlice
return struct{}{}
}()
// MapTo try to map any interface to struct or base type
/*
Eg:
v := map[string]interface{}{
"service":map[string]interface{}{
"ip":"127.0.0.1",
"port":1234,
},
version:"v1.0.01"
}
type Target struct {
Service struct {
Ip string `json:"ip"`
Port int `json:"port"`
} `json:"service"`
Ver string `json:"version"`
}
var dist Target
if err := maputil.MapTo(v,&dist); err != nil {
log.Println(err)
return
}
log.Println(dist)
*/
// Play: https://go.dev/play/p/4K7KBEPgS5M
func MapTo(src any, dst any) error {
dstRef := reflect.ValueOf(dst)
if dstRef.Kind() != reflect.Ptr {
return fmt.Errorf("dst is not ptr")
}
dstElem := dstRef.Type().Elem()
if dstElem.Kind() == reflect.Struct {
srcMap := src.(map[string]interface{})
return MapToStruct(srcMap, dst)
}
dstRef = reflect.Indirect(dstRef)
srcRef := reflect.ValueOf(src)
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
srcRef = srcRef.Elem()
}
if f, ok := mapHandlers[srcRef.Kind()]; ok {
return f(srcRef, dstRef)
}
return fmt.Errorf("no implemented:%s", srcRef.Type())
}
func convertNormal(src reflect.Value, dst reflect.Value) error {
if dst.CanSet() {
if src.Type() == dst.Type() {
dst.Set(src)
} else if src.CanConvert(dst.Type()) {
dst.Set(src.Convert(dst.Type()))
} else {
return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String())
}
}
return nil
}
func convertSlice(src reflect.Value, dst reflect.Value) error {
if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice {
return fmt.Errorf("error type:%s", dst.Type().String())
}
l := src.Len()
target := reflect.MakeSlice(dst.Type(), l, l)
if dst.CanSet() {
dst.Set(target)
}
for i := 0; i < l; i++ {
srcValue := src.Index(i)
if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface {
srcValue = srcValue.Elem()
}
if f, ok := mapHandlers[srcValue.Kind()]; ok {
err := f(srcValue, dst.Index(i))
if err != nil {
return err
}
}
}
return nil
}
func convertMap(src reflect.Value, dst reflect.Value) error {
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
if src.Kind() == reflect.Interface && dst.IsValid() {
return convertMap(src.Elem(), dst)
} else {
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
}
}
dstType := dst.Type()
num := dstType.NumField()
exist := map[string]int{}
for i := 0; i < num; i++ {
k := dstType.Field(i).Tag.Get("json")
if k == "" {
k = dstType.Field(i).Name
}
if strings.Contains(k, ",") {
taglist := strings.Split(k, ",")
if taglist[0] == "" {
k = dstType.Field(i).Name
} else {
k = taglist[0]
}
}
exist[k] = i
}
keys := src.MapKeys()
for _, key := range keys {
if index, ok := exist[key.String()]; ok {
v := dst.Field(index)
if v.Kind() == reflect.Struct {
err := convertMap(src.MapIndex(key), v)
if err != nil {
return err
}
} else {
if v.CanSet() {
if v.Type() == src.MapIndex(key).Elem().Type() {
v.Set(src.MapIndex(key).Elem())
} else if src.MapIndex(key).Elem().CanConvert(v.Type()) {
v.Set(src.MapIndex(key).Elem().Convert(v.Type()))
} else if f, ok := mapHandlers[src.MapIndex(key).Elem().Kind()]; ok && f != nil {
err := f(src.MapIndex(key).Elem(), v)
if err != nil {
return err
}
} else {
return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type())
}
}
}
}
}
return nil
}

View File

@@ -1,125 +0,0 @@
package maputil
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
type (
Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Address *Address `json:"address"`
}
Address struct {
Street string `json:"street"`
Number int `json:"number"`
}
)
func TestStructType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestStructType")
src := map[string]interface{}{
"name": "Nothin",
"age": 28,
"phone": "123456789",
"address": map[string]interface{}{
"street": "test",
"number": 1,
},
}
var p Person
err := MapTo(src, &p)
assert.IsNil(err)
assert.Equal(src["name"], p.Name)
assert.Equal(src["age"], p.Age)
assert.Equal(src["phone"], p.Phone)
assert.Equal("test", p.Address.Street)
assert.Equal(1, p.Address.Number)
}
func TestBaseType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBaseType")
tc := map[string]interface{}{
"i32": -32,
"i8": -8,
"i16": -16,
"i64": -64,
"i": -1,
"u32": 32,
"u8": 8,
"u16": 16,
"u64": 64,
"u": 1,
"tf": true,
"f32": 1.32,
"f64": 1.64,
"str": "hello mapto",
"complex": 1 + 3i,
}
type BaseType struct {
I int `json:"i"`
I8 int8 `json:"i8"`
I16 int16 `json:"i16"`
I32 int32 `json:"i32"`
I64 int64 `json:"i64"`
U uint `json:"u"`
U8 uint8 `json:"u8"`
U16 uint16 `json:"u16"`
U32 uint32 `json:"u32"`
U64 uint64 `json:"u64"`
F32 float32 `json:"f32"`
F64 float64 `json:"f64"`
Tf bool `json:"tf"`
Str string `json:"str"`
Comp complex128 `json:"complex"`
}
var dist BaseType
err := MapTo(tc, &dist)
assert.IsNil(err)
var number float64
MapTo(tc["i"], &number)
assert.EqualValues(-1, number)
MapTo(tc["i8"], &number)
assert.EqualValues(-8, number)
MapTo(tc["i16"], &number)
assert.EqualValues(-16, number)
MapTo(tc["i32"], &number)
assert.EqualValues(-32, number)
MapTo(tc["i64"], &number)
assert.EqualValues(-64, number)
MapTo(tc["u"], &number)
assert.EqualValues(1, number)
MapTo(tc["u8"], &number)
assert.EqualValues(8, number)
MapTo(tc["u16"], &number)
assert.EqualValues(16, number)
MapTo(tc["u32"], &number)
assert.EqualValues(32, number)
MapTo(tc["u64"], &number)
assert.EqualValues(64, number)
}

View File

@@ -21,21 +21,48 @@ import (
// GetInternalIp return internal ipv4.
// Play: https://go.dev/play/p/5mbu-gFp7ei
func GetInternalIp() string {
addr, err := net.InterfaceAddrs()
addrs, err := net.InterfaceAddrs()
if err != nil {
panic(err.Error())
}
for _, a := range addr {
if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
if ipNet.IP.To4() != nil {
return ipNet.IP.String()
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip != nil && (ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast()) {
continue
}
if ipv4 := ip.To4(); ipv4 != nil {
return ipv4.String()
}
}
return ""
}
// func GetInternalIp() string {
// addr, err := net.InterfaceAddrs()
// if err != nil {
// panic(err.Error())
// }
// for _, a := range addr {
// if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
// if ipNet.IP.To4() != nil {
// return ipNet.IP.String()
// }
// }
// }
// return ""
// }
// GetRequestPublicIp return the requested public ip.
// Play: https://go.dev/play/p/kxU-YDc_eBo
func GetRequestPublicIp(req *http.Request) string {

View File

@@ -1386,3 +1386,15 @@ func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
return paddedSlice
}
// Frequency counts the frequency of each element in the slice.
// Play: todo
func Frequency[T comparable](slice []T) map[T]int {
result := make(map[T]int)
for _, v := range slice {
result[v]++
}
return result
}

View File

@@ -1251,3 +1251,13 @@ func ExampleMapConcurrent() {
// Output:
// [1 4 9 16 25 36]
}
func ExampleFrequency() {
strs := []string{"a", "b", "b", "c", "c", "c"}
result := Frequency(strs)
fmt.Println(result)
// Output:
// map[a:1 b:2 c:3]
}

View File

@@ -16,12 +16,20 @@ func TestContain(t *testing.T) {
assert := internal.NewAssert(t, "TestContain")
assert.Equal(true, Contain([]string{"a", "b", "c"}, "a"))
assert.Equal(false, Contain([]string{"a", "b", "c"}, "d"))
assert.Equal(true, Contain([]string{""}, ""))
assert.Equal(false, Contain([]string{}, ""))
tests := []struct {
slice []string
give string
want bool
}{
{[]string{"a", "b", "c"}, "a", true},
{[]string{"a", "b", "c"}, "d", false},
{[]string{""}, "", true},
{[]string{}, "", false},
}
assert.Equal(true, Contain([]int{1, 2, 3}, 1))
for _, tt := range tests {
assert.Equal(tt.want, Contain(tt.slice, tt.give))
}
}
func TestContainBy(t *testing.T) {
@@ -30,32 +38,53 @@ func TestContainBy(t *testing.T) {
assert := internal.NewAssert(t, "TestContainBy")
type foo struct {
A string
B int
a string
b int
}
array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}}
result1 := ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 })
result2 := ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 })
tests := []struct {
slice []foo
predicateFn func(f foo) bool
want bool
}{
{
[]foo{{a: "1", b: 1}, {a: "2", b: 2}},
func(f foo) bool { return f.a == "1" && f.b == 1 },
true,
},
{
[]foo{{a: "1", b: 1}, {a: "2", b: 2}},
func(f foo) bool { return f.a == "2" && f.b == 1 },
false,
},
}
array2 := []string{"a", "b", "c"}
result3 := ContainBy(array2, func(t string) bool { return t == "a" })
result4 := ContainBy(array2, func(t string) bool { return t == "d" })
assert.Equal(true, result1)
assert.Equal(false, result2)
assert.Equal(true, result3)
assert.Equal(false, result4)
for _, tt := range tests {
assert.Equal(tt.want, ContainBy(tt.slice, tt.predicateFn))
}
}
func TestContainSubSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestContainSubSlice")
assert.Equal(true, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "a"}))
assert.Equal(false, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "d"}))
assert.Equal(true, ContainSubSlice([]int{1, 2, 3}, []int{1, 2}))
assert.Equal(false, ContainSubSlice([]int{1, 2, 3}, []int{0, 1}))
tests := []struct {
slice []string
subSlice []string
want bool
}{
{[]string{"a", "b", "c"}, []string{"a", "b"}, true},
{[]string{"a", "b", "c"}, []string{"a", "d"}, false},
{[]string{"a", "b", "c"}, []string{"a", "b", "c"}, true},
{[]string{"a", "b", "c"}, []string{"a", "b", "c", "d"}, false},
{[]string{"a", "b", ""}, []string{"a", ""}, true},
{[]string{""}, []string{""}, true},
}
for _, tt := range tests {
assert.Equal(tt.want, ContainSubSlice(tt.slice, tt.subSlice))
}
}
func TestChunk(t *testing.T) {
@@ -63,29 +92,24 @@ func TestChunk(t *testing.T) {
assert := internal.NewAssert(t, "TestChunk")
arr := []string{"a", "b", "c", "d", "e"}
tests := []struct {
slice []string
chuanSize int
want [][]string
}{
{[]string{"a", "b", "c", "d", "e"}, -1, [][]string{}},
{[]string{"a", "b", "c", "d", "e"}, 0, [][]string{}},
{[]string{"a", "b", "c", "d", "e"}, 1, [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}},
{[]string{"a", "b", "c", "d", "e"}, 2, [][]string{{"a", "b"}, {"c", "d"}, {"e"}}},
{[]string{"a", "b", "c", "d", "e"}, 3, [][]string{{"a", "b", "c"}, {"d", "e"}}},
{[]string{"a", "b", "c", "d", "e"}, 4, [][]string{{"a", "b", "c", "d"}, {"e"}}},
{[]string{"a", "b", "c", "d", "e"}, 5, [][]string{{"a", "b", "c", "d", "e"}}},
{[]string{"a", "b", "c", "d", "e"}, 6, [][]string{{"a", "b", "c", "d", "e"}}},
}
assert.Equal([][]string{}, Chunk(arr, -1))
assert.Equal([][]string{}, Chunk(arr, 0))
r1 := [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
assert.Equal(r1, Chunk(arr, 1))
r2 := [][]string{{"a", "b"}, {"c", "d"}, {"e"}}
assert.Equal(r2, Chunk(arr, 2))
r3 := [][]string{{"a", "b", "c"}, {"d", "e"}}
assert.Equal(r3, Chunk(arr, 3))
r4 := [][]string{{"a", "b", "c", "d"}, {"e"}}
assert.Equal(r4, Chunk(arr, 4))
r5 := [][]string{{"a", "b", "c", "d", "e"}}
assert.Equal(r5, Chunk(arr, 5))
r6 := [][]string{{"a", "b", "c", "d", "e"}}
assert.Equal(r6, Chunk(arr, 6))
for _, tt := range tests {
assert.Equal(tt.want, Chunk(tt.slice, tt.chuanSize))
}
}
func TestCompact(t *testing.T) {
@@ -134,6 +158,7 @@ func TestEqual(t *testing.T) {
assert.Equal(true, Equal(slice1, slice2))
assert.Equal(false, Equal(slice1, slice3))
assert.Equal(false, Equal(slice2, slice3))
}
// go test -fuzz=Fuzz -fuzztime=10s .
@@ -163,12 +188,27 @@ func TestEvery(t *testing.T) {
assert := internal.NewAssert(t, "TestEvery")
nums := []int{1, 2, 3, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
isOdd := func(i, num int) bool {
return num%2 == 1
}
assert.Equal(false, Every(nums, isEven))
tests := []struct {
slice []int
predicateFn func(i, num int) bool
want bool
}{
{[]int{1, 3, 5, 7}, isOdd, true},
{[]int{2, 4, 6, 8}, isEven, true},
{[]int{1, 2, 3, 4}, isOdd, false},
{[]int{1, 2, 3, 4}, isEven, false},
}
for _, tt := range tests {
assert.Equal(tt.want, Every(tt.slice, tt.predicateFn))
}
}
func TestNone(t *testing.T) {
@@ -176,12 +216,27 @@ func TestNone(t *testing.T) {
assert := internal.NewAssert(t, "TestNone")
nums := []int{1, 2, 3, 5}
check := func(i, num int) bool {
isEven := func(i, num int) bool {
return num%2 == 0
}
isOdd := func(i, num int) bool {
return num%2 == 1
}
assert.Equal(false, None(nums, check))
tests := []struct {
slice []int
predicateFn func(i, num int) bool
want bool
}{
{[]int{1, 3, 5, 7}, isEven, true},
{[]int{2, 4, 6, 8}, isOdd, true},
{[]int{1, 2, 3, 4}, isOdd, false},
{[]int{1, 2, 3, 4}, isEven, false},
}
for _, tt := range tests {
assert.Equal(tt.want, None(tt.slice, tt.predicateFn))
}
}
func TestSome(t *testing.T) {
@@ -189,12 +244,27 @@ func TestSome(t *testing.T) {
assert := internal.NewAssert(t, "TestSome")
nums := []int{1, 2, 3, 5}
hasEven := func(i, num int) bool {
isEven := func(i, num int) bool {
return num%2 == 0
}
isOdd := func(i, num int) bool {
return num%2 == 1
}
assert.Equal(true, Some(nums, hasEven))
tests := []struct {
slice []int
predicateFn func(i, num int) bool
want bool
}{
{[]int{1, 3, 5, 7}, isEven, false},
{[]int{2, 4, 6, 8}, isOdd, false},
{[]int{1, 2, 3, 4}, isOdd, true},
{[]int{1, 2, 3, 4}, isEven, true},
}
for _, tt := range tests {
assert.Equal(tt.want, Some(tt.slice, tt.predicateFn))
}
}
func TestFilter(t *testing.T) {
@@ -202,33 +272,37 @@ func TestFilter(t *testing.T) {
assert := internal.NewAssert(t, "TestFilter")
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
t.Run("filter int slice", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
assert.Equal([]int{2, 4}, Filter(nums, isEven))
assert.Equal([]int{2, 4}, Filter(nums, isEven))
})
type student struct {
name string
age int
}
students := []student{
{"a", 10},
{"b", 11},
{"c", 12},
{"d", 13},
{"e", 14},
}
studentsOfAageGreat12 := []student{
{"d", 13},
{"e", 14},
}
filterFunc := func(i int, s student) bool {
return s.age > 12
}
t.Run("filter struct slice", func(t *testing.T) {
type student struct {
name string
age int
}
students := []student{
{"a", 10},
{"b", 11},
{"c", 12},
{"d", 13},
{"e", 14},
}
studentsOfAgeGreat12 := []student{
{"d", 13},
{"e", 14},
}
filterFunc := func(i int, s student) bool {
return s.age > 12
}
assert.Equal(studentsOfAageGreat12, Filter(students, filterFunc))
assert.Equal(studentsOfAgeGreat12, Filter(students, filterFunc))
})
}
func TestGroupBy(t *testing.T) {
@@ -249,6 +323,8 @@ func TestGroupBy(t *testing.T) {
func TestGroupWith(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGroupWith")
nums := []float64{6.1, 4.2, 6.3}
floor := func(num float64) float64 {
return math.Floor(num)
@@ -257,9 +333,8 @@ func TestGroupWith(t *testing.T) {
4: {4.2},
6: {6.1, 6.3},
}
actual := GroupWith(nums, floor)
assert := internal.NewAssert(t, "TestGroupWith")
assert.Equal(expected, actual)
assert.Equal(expected, GroupWith(nums, floor))
}
func TestCount(t *testing.T) {
@@ -295,8 +370,15 @@ func TestFind(t *testing.T) {
result, ok := Find(nums, even)
assert := internal.NewAssert(t, "TestFind")
assert.Equal(true, ok)
assert.Equal(2, *result)
_, ok = Find(nums, func(_ int, v int) bool {
return v == 6
})
assert.Equal(false, ok)
}
func TestFindBy(t *testing.T) {
@@ -358,19 +440,6 @@ func TestFindLast(t *testing.T) {
assert.Equal(4, *result)
}
func TestFindFoundNothing(t *testing.T) {
t.Parallel()
nums := []int{1, 1, 1, 1, 1, 1}
findFunc := func(i, num int) bool {
return num > 1
}
_, ok := Find(nums, findFunc)
assert := internal.NewAssert(t, "TestFindFoundNothing")
assert.Equal(false, ok)
}
func TestFlatten(t *testing.T) {
t.Parallel()
@@ -633,14 +702,12 @@ func TestReduceBy(t *testing.T) {
result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int {
return agg + item
})
assert.Equal(10, result1)
result2 := ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string {
return agg + fmt.Sprintf("%v", item)
})
assert.Equal(10, result1)
assert.Equal("1234", result2)
}
func TestReduceRight(t *testing.T) {
@@ -692,15 +759,23 @@ func TestDeleteAt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDeleteAt")
arr := []int{1, 2, 3, 4, 5}
assert.Equal([]int{2, 3, 4, 5}, DeleteAt(arr, 0))
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 4))
tests := []struct {
slice []int
deletePos int
wang []int
}{
{[]int{1, 2, 3, 4, 5}, 0, []int{2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 1, []int{1, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 2, []int{1, 2, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 3, []int{1, 2, 3, 5}},
{[]int{1, 2, 3, 4, 5}, 4, []int{1, 2, 3, 4}},
{[]int{1, 2, 3, 4, 5}, 5, []int{1, 2, 3, 4}},
}
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 5))
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 6))
assert.Equal([]int{1, 2, 3, 4, 5}, arr)
for _, tt := range tests {
assert.Equal(tt.wang, DeleteAt(tt.slice, tt.deletePos))
}
}
func TestDeleteRange(t *testing.T) {
@@ -720,16 +795,24 @@ func TestDrop(t *testing.T) {
assert := internal.NewAssert(t, "TestDrop")
assert.Equal([]int{}, Drop([]int{}, 0))
assert.Equal([]int{}, Drop([]int{}, 1))
assert.Equal([]int{}, Drop([]int{}, -1))
tests := []struct {
slice []int
dropNum int
want []int
}{
{[]int{}, 0, []int{}},
{[]int{}, 1, []int{}},
{[]int{}, -1, []int{}},
{[]int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 0, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 1, []int{2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 5, []int{}},
{[]int{1, 2, 3, 4, 5}, 6, []int{}},
}
assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 0))
assert.Equal([]int{2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 1))
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 5))
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 6))
assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, -1))
for _, tt := range tests {
assert.Equal(tt.want, Drop(tt.slice, tt.dropNum))
}
}
func TestDropRight(t *testing.T) {
@@ -737,16 +820,23 @@ func TestDropRight(t *testing.T) {
assert := internal.NewAssert(t, "TestDropRight")
assert.Equal([]int{}, DropRight([]int{}, 0))
assert.Equal([]int{}, DropRight([]int{}, 1))
assert.Equal([]int{}, DropRight([]int{}, -1))
assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, 0))
assert.Equal([]int{1, 2, 3, 4}, DropRight([]int{1, 2, 3, 4, 5}, 1))
assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 5))
assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 6))
assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, -1))
tests := []struct {
slice []int
dropNum int
want []int
}{
{[]int{}, 0, []int{}},
{[]int{}, 1, []int{}},
{[]int{}, -1, []int{}},
{[]int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 0, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 1, []int{1, 2, 3, 4}},
{[]int{}, 5, []int{}},
{[]int{}, 6, []int{}},
}
for _, tt := range tests {
assert.Equal(tt.want, DropRight(tt.slice, tt.dropNum))
}
}
func TestDropWhile(t *testing.T) {
@@ -754,22 +844,19 @@ func TestDropWhile(t *testing.T) {
assert := internal.NewAssert(t, "TestDropWhile")
numbers := []int{1, 2, 3, 4, 5}
tests := []struct {
slice []int
fn func(int) bool
want []int
}{
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n != 2 }, []int{2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return true }, []int{}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 0 }, []int{1, 2, 3, 4, 5}},
}
r1 := DropWhile(numbers, func(n int) bool {
return n != 2
})
assert.Equal([]int{2, 3, 4, 5}, r1)
r2 := DropWhile(numbers, func(n int) bool {
return true
})
assert.Equal([]int{}, r2)
r3 := DropWhile(numbers, func(n int) bool {
return n == 0
})
assert.Equal([]int{1, 2, 3, 4, 5}, r3)
for _, tt := range tests {
assert.Equal(tt.want, DropWhile(tt.slice, tt.fn))
}
}
func TestDropRightWhile(t *testing.T) {
@@ -777,22 +864,19 @@ func TestDropRightWhile(t *testing.T) {
assert := internal.NewAssert(t, "TestDropRightWhile")
numbers := []int{1, 2, 3, 4, 5}
tests := []struct {
slice []int
fn func(int) bool
want []int
}{
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n != 2 }, []int{1, 2}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return true }, []int{}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 0 }, []int{1, 2, 3, 4, 5}},
}
r1 := DropRightWhile(numbers, func(n int) bool {
return n != 2
})
assert.Equal([]int{1, 2}, r1)
r2 := DropRightWhile(numbers, func(n int) bool {
return true
})
assert.Equal([]int{}, r2)
r3 := DropRightWhile(numbers, func(n int) bool {
return n == 0
})
assert.Equal([]int{1, 2, 3, 4, 5}, r3)
for _, tt := range tests {
assert.Equal(tt.want, DropRightWhile(tt.slice, tt.fn))
}
}
func TestInsertAt(t *testing.T) {
@@ -800,15 +884,25 @@ func TestInsertAt(t *testing.T) {
assert := internal.NewAssert(t, "TestInsertAt")
strs := []string{"a", "b", "c"}
assert.Equal([]string{"a", "b", "c"}, InsertAt(strs, -1, "1"))
assert.Equal([]string{"a", "b", "c"}, InsertAt(strs, 4, "1"))
assert.Equal([]string{"1", "a", "b", "c"}, InsertAt(strs, 0, "1"))
assert.Equal([]string{"a", "1", "b", "c"}, InsertAt(strs, 1, "1"))
assert.Equal([]string{"a", "b", "1", "c"}, InsertAt(strs, 2, "1"))
assert.Equal([]string{"a", "b", "c", "1"}, InsertAt(strs, 3, "1"))
assert.Equal([]string{"1", "2", "3", "a", "b", "c"}, InsertAt(strs, 0, []string{"1", "2", "3"}))
assert.Equal([]string{"a", "b", "c", "1", "2", "3"}, InsertAt(strs, 3, []string{"1", "2", "3"}))
tests := []struct {
slice []string
insertPos int
insertValue any
want []string
}{
{[]string{"a", "b", "c"}, -1, "1", []string{"a", "b", "c"}},
{[]string{"a", "b", "c"}, 4, "1", []string{"a", "b", "c"}},
{[]string{"a", "b", "c"}, 0, "1", []string{"1", "a", "b", "c"}},
{[]string{"a", "b", "c"}, 1, "1", []string{"a", "1", "b", "c"}},
{[]string{"a", "b", "c"}, 2, "1", []string{"a", "b", "1", "c"}},
{[]string{"a", "b", "c"}, 3, "1", []string{"a", "b", "c", "1"}},
{[]string{"a", "b", "c"}, 0, []string{"1", "2", "3"}, []string{"1", "2", "3", "a", "b", "c"}},
{[]string{"a", "b", "c"}, 3, []string{"1", "2", "3"}, []string{"a", "b", "c", "1", "2", "3"}},
}
for _, tt := range tests {
assert.Equal(tt.want, InsertAt(tt.slice, tt.insertPos, tt.insertValue))
}
}
func TestUpdateAt(t *testing.T) {
@@ -1670,5 +1764,55 @@ func TestFilterConcurrent(t *testing.T) {
actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 4)
assert.Equal(expected, actual)
})
}
func TestFrequency(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFrequency")
t.Run("empty slice", func(t *testing.T) {
result := Frequency([]int{})
assert.Equal(map[int]int{}, result)
})
t.Run("int slice", func(t *testing.T) {
nums := []int{1, 2, 2, 3, 3, 3}
expected := map[int]int{1: 1, 2: 2, 3: 3}
result := Frequency(nums)
assert.Equal(expected, result)
})
t.Run("string slice", func(t *testing.T) {
strs := []string{"a", "b", "b", "c", "c", "c"}
expected := map[string]int{"a": 1, "b": 2, "c": 3}
result := Frequency(strs)
assert.Equal(expected, result)
})
t.Run("struct slice", func(t *testing.T) {
type student struct {
Name string
Age int
}
students := []student{
{Name: "a", Age: 11},
{Name: "b", Age: 12},
{Name: "a", Age: 13},
{Name: "c", Age: 14},
}
expected := map[student]int{
{Name: "a", Age: 11}: 1,
{Name: "a", Age: 13}: 1,
{Name: "b", Age: 12}: 1,
{Name: "c", Age: 14}: 1,
}
result := Frequency(students)
assert.Equal(expected, result)
})
}

View File

@@ -5,13 +5,18 @@ package strutil
import (
"errors"
"math/rand"
"regexp"
"strings"
"time"
"unicode"
"unicode/utf8"
"unsafe"
)
// used in `Shuffle` function
var rng = rand.New(rand.NewSource(int64(time.Now().UnixNano())))
// CamelCase coverts string to camelCase string. Non letters and numbers will be ignored.
// Play: https://go.dev/play/p/9eXP3tn2tUy
func CamelCase(s string) string {
@@ -70,7 +75,7 @@ func LowerFirst(s string) string {
return string(r) + s[size:]
}
// PadStart pads string on the left and right side if it's shorter than size.
// Pad pads string on the left and right side if it's shorter than size.
// Padding characters are truncated if they exceed size.
// Play: https://go.dev/play/p/NzImQq-VF8q
func Pad(source string, size int, padStr string) string {
@@ -621,6 +626,8 @@ func HammingDistance(a, b string) (int, error) {
// Concat uses the strings.Builder to concatenate the input strings.
// - `length` is the expected length of the concatenated string.
// - if you are unsure about the length of the string to be concatenated, please pass 0 or a negative number.
//
// Play: todo
func Concat(length int, str ...string) string {
if len(str) == 0 {
return ""
@@ -638,3 +645,93 @@ func Concat(length int, str ...string) string {
}
return sb.String()
}
// Ellipsis truncates a string to a specified length and appends an ellipsis.
// Play: todo
func Ellipsis(str string, length int) string {
str = strings.TrimSpace(str)
if length <= 0 {
return ""
}
runes := []rune(str)
if len(runes) <= length {
return str
}
return string(runes[:length]) + "..."
}
// Shuffle the order of characters of given string.
// Play: todo
func Shuffle(str string) string {
runes := []rune(str)
for i := len(runes) - 1; i > 0; i-- {
j := rng.Intn(i + 1)
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
// Rotate rotates the string by the specified number of characters.
// Play: todo
func Rotate(str string, shift int) string {
if shift == 0 {
return str
}
runes := []rune(str)
length := len(runes)
if length == 0 {
return str
}
shift = shift % length
if shift < 0 {
shift = length + shift
}
var sb strings.Builder
sb.Grow(length)
sb.WriteString(string(runes[length-shift:]))
sb.WriteString(string(runes[:length-shift]))
return sb.String()
}
// TemplateReplace replaces the placeholders in the template string with the corresponding values in the data map.
// The placeholders are enclosed in curly braces, e.g. {key}.
// for example, the template string is "Hello, {name}!", and the data map is {"name": "world"},
// the result will be "Hello, world!".
// Play: todo
func TemplateReplace(template string, data map[string]string) string {
re := regexp.MustCompile(`\{(\w+)\}`)
result := re.ReplaceAllStringFunc(template, func(s string) string {
key := strings.Trim(s, "{}")
if val, ok := data[key]; ok {
return val
}
return s
})
result = strings.ReplaceAll(result, "{{", "{")
result = strings.ReplaceAll(result, "}}", "}")
return result
}
// RegexMatchAllGroups Matches all subgroups in a string using a regular expression and returns the result.
// Play: todo
func RegexMatchAllGroups(pattern, str string) [][]string {
re := regexp.MustCompile(pattern)
matches := re.FindAllStringSubmatch(str, -1)
return matches
}

View File

@@ -694,3 +694,62 @@ func ExampleConcat() {
// Go Language
// An apple a daykeeps the doctor away
}
func ExampleEllipsis() {
result1 := Ellipsis("hello world", 5)
result2 := Ellipsis("你好,世界!", 2)
result3 := Ellipsis("😀😃😄😁😆", 3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// hello...
// 你好...
// 😀😃😄...
}
func ExampleRotate() {
result1 := Rotate("Hello", 0)
result2 := Rotate("Hello", 1)
result3 := Rotate("Hello", 2)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello
// oHell
// loHel
}
func ExampleTemplateReplace() {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
result := TemplateReplace(template, data)
fmt.Println(result)
// Output:
// Hello, my name is Bob, I'm 20 years old.
}
func ExampleRegexMatchAllGroups() {
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
str := "Emails: john.doe@example.com and jane.doe@example.com"
result := RegexMatchAllGroups(pattern, str)
fmt.Println(result[0])
fmt.Println(result[1])
// Output:
// [john.doe@example.com john.doe example com]
// [jane.doe@example.com jane.doe example com]
}

View File

@@ -1,7 +1,6 @@
package strutil
import (
"reflect"
"testing"
"github.com/duke-git/lancet/v2/internal"
@@ -12,19 +11,22 @@ func TestCamelCase(t *testing.T) {
assert := internal.NewAssert(t, "TestCamelCase")
cases := map[string]string{
"": "",
"foobar": "foobar",
"&FOO:BAR$BAZ": "fooBarBaz",
"fooBar": "fooBar",
"FOObar": "foObar",
"$foo%": "foo",
" $#$Foo 22 bar ": "foo22Bar",
"Foo-#1😄$_%^&*(1bar": "foo11Bar",
tests := []struct {
input string
expected string
}{
{"", ""},
{"foobar", "foobar"},
{"&FOO:BAR$BAZ", "fooBarBaz"},
{"fooBar", "fooBar"},
{"FOObar", "foObar"},
{"$foo%", "foo"},
{" $#$Foo 22 bar ", "foo22Bar"},
{"Foo-#1😄$_%^&*(1bar", "foo11Bar"},
}
for k, v := range cases {
assert.Equal(v, CamelCase(k))
for _, tt := range tests {
assert.Equal(tt.expected, CamelCase(tt.input))
}
}
@@ -33,19 +35,22 @@ func TestCapitalize(t *testing.T) {
assert := internal.NewAssert(t, "TestCapitalize")
cases := map[string]string{
"": "",
"Foo": "Foo",
"_foo": "_foo",
"foobar": "Foobar",
"fooBar": "Foobar",
"foo Bar": "Foo bar",
"foo-bar": "Foo-bar",
"$foo%": "$foo%",
tests := []struct {
input string
expected string
}{
{"", ""},
{"Foo", "Foo"},
{"_foo", "_foo"},
{"foobar", "Foobar"},
{"fooBar", "Foobar"},
{"foo Bar", "Foo bar"},
{"foo-bar", "Foo-bar"},
{"$foo%", "$foo%"},
}
for k, v := range cases {
assert.Equal(v, Capitalize(k))
for _, tt := range tests {
assert.Equal(tt.expected, Capitalize(tt.input))
}
}
@@ -54,23 +59,26 @@ func TestKebabCase(t *testing.T) {
assert := internal.NewAssert(t, "TestKebabCase")
cases := map[string]string{
"": "",
"foo-bar": "foo-bar",
"--Foo---Bar-": "foo-bar",
"Foo Bar-": "foo-bar",
"foo_Bar": "foo-bar",
"fooBar": "foo-bar",
"FOOBAR": "foobar",
"FOO_BAR": "foo-bar",
"__FOO_BAR__": "foo-bar",
"$foo@Bar": "foo-bar",
" $#$Foo 22 bar ": "foo-22-bar",
"Foo-#1😄$_%^&*(1bar": "foo-1-1-bar",
tests := []struct {
input string
expected string
}{
{"", ""},
{"foo-bar", "foo-bar"},
{"--Foo---Bar-", "foo-bar"},
{"Foo Bar-", "foo-bar"},
{"foo_Bar", "foo-bar"},
{"fooBar", "foo-bar"},
{"FOOBAR", "foobar"},
{"FOO_BAR", "foo-bar"},
{"__FOO_BAR__", "foo-bar"},
{"$foo@Bar", "foo-bar"},
{" $#$Foo 22 bar ", "foo-22-bar"},
{"Foo-#1😄$_%^&*(1bar", "foo-1-1-bar"},
}
for k, v := range cases {
assert.Equal(v, KebabCase(k))
for _, tt := range tests {
assert.Equal(tt.expected, KebabCase(tt.input))
}
}
@@ -79,23 +87,26 @@ func TestUpperKebabCase(t *testing.T) {
assert := internal.NewAssert(t, "TestUpperKebabCase")
cases := map[string]string{
"": "",
"foo-bar": "FOO-BAR",
"--Foo---Bar-": "FOO-BAR",
"Foo Bar-": "FOO-BAR",
"foo_Bar": "FOO-BAR",
"fooBar": "FOO-BAR",
"FOOBAR": "FOOBAR",
"FOO_BAR": "FOO-BAR",
"__FOO_BAR__": "FOO-BAR",
"$foo@Bar": "FOO-BAR",
" $#$Foo 22 bar ": "FOO-22-BAR",
"Foo-#1😄$_%^&*(1bar": "FOO-1-1-BAR",
tests := []struct {
input string
expected string
}{
{"", ""},
{"foo-bar", "FOO-BAR"},
{"--Foo---Bar-", "FOO-BAR"},
{"Foo Bar-", "FOO-BAR"},
{"foo_Bar", "FOO-BAR"},
{"fooBar", "FOO-BAR"},
{"FOOBAR", "FOOBAR"},
{"FOO_BAR", "FOO-BAR"},
{"__FOO_BAR__", "FOO-BAR"},
{"$foo@Bar", "FOO-BAR"},
{" $#$Foo 22 bar ", "FOO-22-BAR"},
{"Foo-#1😄$_%^&*(1bar", "FOO-1-1-BAR"},
}
for k, v := range cases {
assert.Equal(v, UpperKebabCase(k))
for _, tt := range tests {
assert.Equal(tt.expected, UpperKebabCase(tt.input))
}
}
@@ -104,23 +115,26 @@ func TestSnakeCase(t *testing.T) {
assert := internal.NewAssert(t, "TestSnakeCase")
cases := map[string]string{
"": "",
"foo-bar": "foo_bar",
"--Foo---Bar-": "foo_bar",
"Foo Bar-": "foo_bar",
"foo_Bar": "foo_bar",
"fooBar": "foo_bar",
"FOOBAR": "foobar",
"FOO_BAR": "foo_bar",
"__FOO_BAR__": "foo_bar",
"$foo@Bar": "foo_bar",
" $#$Foo 22 bar ": "foo_22_bar",
"Foo-#1😄$_%^&*(1bar": "foo_1_1_bar",
tests := []struct {
input string
expected string
}{
{"", ""},
{"foo-bar", "foo_bar"},
{"--Foo---Bar-", "foo_bar"},
{"Foo Bar-", "foo_bar"},
{"foo_Bar", "foo_bar"},
{"fooBar", "foo_bar"},
{"FOOBAR", "foobar"},
{"FOO_BAR", "foo_bar"},
{"__FOO_BAR__", "foo_bar"},
{"$foo@Bar", "foo_bar"},
{" $#$Foo 22 bar ", "foo_22_bar"},
{"Foo-#1😄$_%^&*(1bar", "foo_1_1_bar"},
}
for k, v := range cases {
assert.Equal(v, SnakeCase(k))
for _, tt := range tests {
assert.Equal(tt.expected, SnakeCase(tt.input))
}
}
@@ -129,23 +143,26 @@ func TestUpperSnakeCase(t *testing.T) {
assert := internal.NewAssert(t, "TestUpperSnakeCase")
cases := map[string]string{
"": "",
"foo-bar": "FOO_BAR",
"--Foo---Bar-": "FOO_BAR",
"Foo Bar-": "FOO_BAR",
"foo_Bar": "FOO_BAR",
"fooBar": "FOO_BAR",
"FOOBAR": "FOOBAR",
"FOO_BAR": "FOO_BAR",
"__FOO_BAR__": "FOO_BAR",
"$foo@Bar": "FOO_BAR",
" $#$Foo 22 bar ": "FOO_22_BAR",
"Foo-#1😄$_%^&*(1bar": "FOO_1_1_BAR",
tests := []struct {
input string
expected string
}{
{"", ""},
{"foo-bar", "FOO_BAR"},
{"--Foo---Bar-", "FOO_BAR"},
{"Foo Bar-", "FOO_BAR"},
{"foo_Bar", "FOO_BAR"},
{"fooBar", "FOO_BAR"},
{"FOOBAR", "FOOBAR"},
{"FOO_BAR", "FOO_BAR"},
{"__FOO_BAR__", "FOO_BAR"},
{"$foo@Bar", "FOO_BAR"},
{" $#$Foo 22 bar ", "FOO_22_BAR"},
{"Foo-#1😄$_%^&*(1bar", "FOO_1_1_BAR"},
}
for k, v := range cases {
assert.Equal(v, UpperSnakeCase(k))
for _, tt := range tests {
assert.Equal(tt.expected, UpperSnakeCase(tt.input))
}
}
@@ -154,16 +171,19 @@ func TestUpperFirst(t *testing.T) {
assert := internal.NewAssert(t, "TestLowerFirst")
cases := map[string]string{
"": "",
"foo": "Foo",
"bAR": "BAR",
"FOo": "FOo",
"fOo大": "FOo大",
tests := []struct {
input string
expected string
}{
{"", ""},
{"foo", "Foo"},
{"bAR", "BAR"},
{"FOo", "FOo"},
{"fOo大", "FOo大"},
}
for k, v := range cases {
assert.Equal(v, UpperFirst(k))
for _, tt := range tests {
assert.Equal(tt.expected, UpperFirst(tt.input))
}
}
@@ -172,16 +192,19 @@ func TestLowerFirst(t *testing.T) {
assert := internal.NewAssert(t, "TestLowerFirst")
cases := map[string]string{
"": "",
"foo": "foo",
"bAR": "bAR",
"FOo": "fOo",
"fOo大": "fOo大",
tests := []struct {
input string
expected string
}{
{"", ""},
{"foo", "foo"},
{"bAR", "bAR"},
{"FOo", "fOo"},
{"fOo大", "fOo大"},
}
for k, v := range cases {
assert.Equal(v, LowerFirst(k))
for _, tt := range tests {
assert.Equal(tt.expected, LowerFirst(tt.input))
}
}
@@ -190,23 +213,48 @@ func TestPad(t *testing.T) {
assert := internal.NewAssert(t, "TestPad")
assert.Equal("a ", Pad("a", 2, ""))
assert.Equal("a", Pad("a", 1, "b"))
assert.Equal("ab", Pad("a", 2, "b"))
assert.Equal("mabcdm", Pad("abcd", 6, "m"))
tests := []struct {
input string
padSize int
padChar string
expected string
}{
{"", 0, "", ""},
{"a", 0, "", "a"},
{"a", 1, "", "a"},
{"a", 2, "", "a "},
{"a", 1, "b", "a"},
{"a", 2, "b", "ab"},
{"abcd", 6, "m", "mabcdm"},
}
for _, tt := range tests {
assert.Equal(tt.expected, Pad(tt.input, tt.padSize, tt.padChar))
}
}
func TestPadEnd(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPadEnd")
assert.Equal("a ", PadEnd("a", 2, " "))
assert.Equal("a", PadEnd("a", 1, "b"))
assert.Equal("ab", PadEnd("a", 2, "b"))
assert.Equal("abcdmn", PadEnd("abcd", 6, "mno"))
assert.Equal("abcdmm", PadEnd("abcd", 6, "m"))
assert.Equal("abcaba", PadEnd("abc", 6, "ab"))
tests := []struct {
input string
padSize int
padChar string
expected string
}{
{"a", 2, " ", "a "},
{"a", 1, "b", "a"},
{"a", 2, "b", "ab"},
{"abcd", 6, "mno", "abcdmn"},
{"abcd", 6, "m", "abcdmm"},
{"abcd", 6, "ab", "abcdab"},
}
assert.NotEqual("ba", PadEnd("a", 2, "b"))
for _, tt := range tests {
assert.Equal(tt.expected, PadEnd(tt.input, tt.padSize, tt.padChar))
}
}
func TestPadStart(t *testing.T) {
@@ -214,13 +262,22 @@ func TestPadStart(t *testing.T) {
assert := internal.NewAssert(t, "TestPadStart")
assert.Equal("a", PadStart("a", 1, "b"))
assert.Equal("ba", PadStart("a", 2, "b"))
assert.Equal("mnabcd", PadStart("abcd", 6, "mno"))
assert.Equal("mmabcd", PadStart("abcd", 6, "m"))
assert.Equal("abaabc", PadStart("abc", 6, "ab"))
tests := []struct {
input string
padSize int
padChar string
expected string
}{
{"a", 1, "b", "a"},
{"a", 2, "b", "ba"},
{"abcd", 6, "mno", "mnabcd"},
{"abcd", 6, "m", "mmabcd"},
{"abc", 6, "ab", "abaabc"},
}
assert.NotEqual("ab", PadStart("a", 2, "b"))
for _, tt := range tests {
assert.Equal(tt.expected, PadStart(tt.input, tt.padSize, tt.padChar))
}
}
func TestBefore(t *testing.T) {
@@ -228,12 +285,21 @@ func TestBefore(t *testing.T) {
assert := internal.NewAssert(t, "TestBefore")
assert.Equal("lancet", Before("lancet", ""))
assert.Equal("", Before("lancet", "lancet"))
assert.Equal("lancet", Before("lancet", "abcdef"))
tests := []struct {
input string
char string
expected string
}{
{"lancet", "", "lancet"},
{"lancet", "lancet", ""},
{"lancet", "abcdef", "lancet"},
{"github.com/test/lancet", "/", "github.com"},
{"github.com/test/lancet", "test", "github.com/"},
}
assert.Equal("github.com", Before("github.com/test/lancet", "/"))
assert.Equal("github.com/", Before("github.com/test/lancet", "test"))
for _, tt := range tests {
assert.Equal(tt.expected, Before(tt.input, tt.char))
}
}
func TestBeforeLast(t *testing.T) {
@@ -243,7 +309,6 @@ func TestBeforeLast(t *testing.T) {
assert.Equal("lancet", BeforeLast("lancet", ""))
assert.Equal("lancet", BeforeLast("lancet", "abcdef"))
assert.Equal("github.com/test", BeforeLast("github.com/test/lancet", "/"))
assert.Equal("github.com/test/", BeforeLast("github.com/test/test/lancet", "test"))
}
@@ -257,7 +322,6 @@ func TestAfter(t *testing.T) {
assert.Equal("", After("lancet", "lancet"))
assert.Equal("test/lancet", After("github.com/test/lancet", "/"))
assert.Equal("/lancet", After("github.com/test/lancet", "test"))
assert.Equal("lancet", After("lancet", "abcdef"))
}
@@ -403,10 +467,8 @@ func TestStringToBytes(t *testing.T) {
assert := internal.NewAssert(t, "TestStringToBytes")
str := "abc"
bytes := StringToBytes(str)
assert.Equal(reflect.DeepEqual(bytes, []byte{'a', 'b', 'c'}), true)
bytes := StringToBytes("abc")
assert.Equal(bytes, []byte{'a', 'b', 'c'})
}
func TestBytesToString(t *testing.T) {
@@ -414,16 +476,15 @@ func TestBytesToString(t *testing.T) {
assert := internal.NewAssert(t, "TestBytesToString")
bytes := []byte{'a', 'b', 'c'}
str := BytesToString(bytes)
assert.Equal(str == "abc", true)
str := BytesToString([]byte{'a', 'b', 'c'})
assert.Equal("abc", str)
}
func TestIsBlank(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsBlank")
assert.Equal(IsBlank(""), true)
assert.Equal(IsBlank("\t\v\f\n"), true)
@@ -434,6 +495,7 @@ func TestIsNotBlank(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsBlank")
assert.Equal(IsNotBlank(""), false)
assert.Equal(IsNotBlank(" "), false)
assert.Equal(IsNotBlank("\t\v\f\n"), false)
@@ -446,6 +508,7 @@ func TestHasPrefixAny(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHasPrefixAny")
str := "foo bar"
prefixes := []string{"fo", "xyz", "hello"}
notMatches := []string{"oom", "world"}
@@ -458,6 +521,7 @@ func TestHasSuffixAny(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHasSuffixAny")
str := "foo bar"
suffixes := []string{"bar", "xyz", "hello"}
notMatches := []string{"oom", "world"}
@@ -503,6 +567,7 @@ func TestTrim(t *testing.T) {
assert.Equal("$ ab cd $", Trim(str1))
assert.Equal("ab cd", Trim(str1, "$"))
assert.Equal("abcd", Trim("\nabcd"))
}
@@ -525,19 +590,27 @@ func TestHideString(t *testing.T) {
assert := internal.NewAssert(t, "TestTrim")
str := "13242658976"
tests := []struct {
input string
start int
end int
replacedChar string
expected string
}{
{"13242658976", 0, -1, "*", "13242658976"},
{"13242658976", 0, 0, "*", "13242658976"},
{"13242658976", 0, 4, "*", "****2658976"},
{"13242658976", 3, 3, "*", "13242658976"},
{"13242658976", 3, 4, "*", "132*2658976"},
{"13242658976", 3, 7, "*", "132****8976"},
{"13242658976", 3, 11, "*", "132********"},
{"13242658976", 7, 100, "*", "1324265****"},
{"13242658976", 100, 100, "*", "13242658976"},
}
assert.Equal("13242658976", HideString(str, 0, -1, "*"))
assert.Equal("13242658976", HideString(str, 0, 0, "*"))
assert.Equal("****2658976", HideString(str, 0, 4, "*"))
assert.Equal("13242658976", HideString(str, 3, 3, "*"))
assert.Equal("132*2658976", HideString(str, 3, 4, "*"))
assert.Equal("132****8976", HideString(str, 3, 7, "*"))
assert.Equal("1324265****", HideString(str, 7, 11, "*"))
assert.Equal("1324265****", HideString(str, 7, 100, "*"))
assert.Equal("13242658976", HideString(str, 100, 100, "*"))
for _, tt := range tests {
assert.Equal(tt.expected, HideString(tt.input, tt.start, tt.end, tt.replacedChar))
}
}
func TestContainsAll(t *testing.T) {
@@ -593,23 +666,34 @@ func TestHammingDistance(t *testing.T) {
return c
}
assert.Equal(0, hd(" ", " "))
assert.Equal(1, hd(" ", "c"))
assert.Equal(1, hd("a", "d"))
assert.Equal(1, hd("a", " "))
assert.Equal(1, hd("a", "f"))
tests := []struct {
strA string
strB string
hammingDistance int
}{
{" ", " ", 0},
{" ", "c", 1},
{"a", "d", 1},
{"a", " ", 1},
{"a", "f", 1},
assert.Equal(0, hd("", ""))
assert.Equal(-1, hd("abc", "ab"))
assert.Equal(3, hd("abc", "def"))
assert.Equal(-1, hd("kitten", "sitting"))
assert.Equal(1, hd("ö", "ü"))
assert.Equal(0, hd("日本語", "日本語"))
assert.Equal(3, hd("日本語", "語日本"))
{"", "", 0},
{"abc", "ab", -1},
{"abc", "def", 3},
{"kitten", "sitting", -1},
{"ö", "ü", 1},
{"日本語", "日本語", 0},
{"日本語", "語日本", 3},
}
for _, tt := range tests {
assert.Equal(tt.hammingDistance, hd(tt.strA, tt.strB))
}
}
func TestConcat(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestConcat")
assert.Equal("", Concat(0))
@@ -620,3 +704,152 @@ func TestConcat(t *testing.T) {
assert.Equal("你好,世界!", Concat(0, "你好", "", "", "世界!", ""))
assert.Equal("Hello World!", Concat(0, "Hello", " Wo", "r", "ld!", ""))
}
func TestEllipsis(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEllipsis")
tests := []struct {
input string
length int
expected string
}{
{"", 0, ""},
{"hello world", 0, ""},
{"hello world", -1, ""},
{"hello world", 5, "hello..."},
{"hello world", 11, "hello world"},
{"你好,世界!", 2, "你好..."},
{"😀😃😄😁😆", 3, "😀😃😄..."},
{"This is a test.", 10, "This is a ..."},
}
for _, tt := range tests {
assert.Equal(tt.expected, Ellipsis(tt.input, tt.length))
}
}
func TestShuffle(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestShuffle")
assert.Equal("", Shuffle(""))
assert.Equal("a", Shuffle("a"))
str := "hello"
shuffledStr := Shuffle(str)
assert.Equal(5, len(shuffledStr))
}
func TestRotate(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRotate")
tests := []struct {
input string
shift int
expected string
}{
{"", 1, ""},
{"a", 0, "a"},
{"a", 1, "a"},
{"a", -1, "a"},
{"Hello", -2, "lloHe"},
{"Hello", 1, "oHell"},
{"Hello, world!", 3, "ld!Hello, wor"},
}
for _, tt := range tests {
assert.Equal(tt.expected, Rotate(tt.input, tt.shift))
}
}
func TestTemplateReplace(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestTemplateReplace")
t.Run("basic", func(t *testing.T) {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
expected := `Hello, my name is Bob, I'm 20 years old.`
result := TemplateReplace(template, data)
assert.Equal(expected, result)
})
t.Run("not found", func(t *testing.T) {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
}
expected := `Hello, my name is Bob, I'm {age} years old.`
result := TemplateReplace(template, data)
assert.Equal(expected, result)
})
t.Run("brackets", func(t *testing.T) {
template := `Hello, my name is {name}, I'm {{age}} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
expected := `Hello, my name is Bob, I'm {20} years old.`
result := TemplateReplace(template, data)
assert.Equal(expected, result)
})
}
func TestRegexMatchAllGroups(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRegexMatchAllGroups")
tests := []struct {
pattern string
str string
expected [][]string
}{
{
pattern: `(\w+\.+\w+)@(\w+)\.(\w+)`,
str: "Emails: john.doe@example.com and jane.doe@example.com",
expected: [][]string{{"john.doe@example.com", "john.doe", "example", "com"}, {"jane.doe@example.com", "jane.doe", "example", "com"}},
},
{
pattern: `(\d+)`,
str: "No numbers here!",
expected: nil,
},
{
pattern: `(\d{3})-(\d{2})-(\d{4})`,
str: "My number is 123-45-6789",
expected: [][]string{{"123-45-6789", "123", "45", "6789"}},
},
{
pattern: `(\w+)\s(\d+)`,
str: "Item A 123, Item B 456",
expected: [][]string{{"A 123", "A", "123"}, {"B 456", "B", "456"}},
},
{
pattern: `(\d{2})-(\d{2})-(\d{4})`,
str: "Dates: 01-01-2020, 12-31-1999",
expected: [][]string{{"01-01-2020", "01", "01", "2020"}, {"12-31-1999", "12", "31", "1999"}},
},
}
for _, tt := range tests {
result := RegexMatchAllGroups(tt.pattern, tt.str)
assert.Equal(tt.expected, result)
}
}

View File

@@ -6,9 +6,12 @@ package system
import (
"bytes"
"fmt"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"unicode/utf8"
"github.com/duke-git/lancet/v2/validator"
@@ -132,3 +135,190 @@ func byteToString(data []byte, charset string) string {
func GetOsBits() int {
return 32 << (^uint(0) >> 63)
}
// StartProcess start a new process with the specified name and arguments.
// Play: todo
func StartProcess(command string, args ...string) (int, error) {
cmd := exec.Command(command, args...)
if err := cmd.Start(); err != nil {
return 0, err
}
return cmd.Process.Pid, nil
}
// StopProcess stop a process by pid.
// Play: todo
func StopProcess(pid int) error {
process, err := os.FindProcess(pid)
if err != nil {
return err
}
return process.Signal(os.Kill)
}
// KillProcess kill a process by pid.
// Play: todo
func KillProcess(pid int) error {
process, err := os.FindProcess(pid)
if err != nil {
return err
}
return process.Kill()
}
// ProcessInfo contains detailed information about a process.
type ProcessInfo struct {
PID int
CPU string
Memory string
State string
User string
Cmd string
Threads []string
IOStats string
StartTime string
ParentPID int
NetworkConnections string
}
// GetProcessInfo retrieves detailed process information by pid.
// Play: todo
func GetProcessInfo(pid int) (*ProcessInfo, error) {
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.Command("tasklist", "/FI", fmt.Sprintf("PID eq %d", pid), "/FO", "CSV", "/V")
} else {
cmd = exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "pid,%cpu,%mem,state,user,comm")
}
output, err := cmd.Output()
if err != nil {
return nil, err
}
processInfo, err := parseProcessInfo(output, pid)
if err != nil {
return nil, err
}
if runtime.GOOS != "windows" {
processInfo.Threads, _ = getThreadsInfo(pid)
processInfo.IOStats, _ = getIOStats(pid)
processInfo.StartTime, _ = getProcessStartTime(pid)
processInfo.ParentPID, _ = getParentProcess(pid)
processInfo.NetworkConnections, _ = getNetworkConnections(pid)
}
return processInfo, nil
}
// parseProcessInfo parses the output of `ps` or `tasklist` to fill the ProcessInfo structure.
func parseProcessInfo(output []byte, pid int) (*ProcessInfo, error) {
lines := strings.Split(string(output), "\n")
if len(lines) < 2 {
return nil, fmt.Errorf("no process found with PID %d", pid)
}
var processInfo ProcessInfo
if runtime.GOOS == "windows" {
fields := strings.Split(lines[1], "\",\"")
if len(fields) < 9 {
return nil, fmt.Errorf("unexpected tasklist output format")
}
processInfo = ProcessInfo{
PID: pid,
CPU: "N/A",
Memory: fields[4], // Memory usage in K
State: fields[5],
User: "N/A",
Cmd: fields[8],
}
} else {
fields := strings.Fields(lines[1])
if len(fields) < 6 {
return nil, fmt.Errorf("unexpected ps output format")
}
processInfo = ProcessInfo{
PID: pid,
CPU: fields[1],
Memory: fields[2],
State: fields[3],
User: fields[4],
Cmd: fields[5],
}
}
return &processInfo, nil
}
func getThreadsInfo(pid int) ([]string, error) {
cmd := exec.Command("ps", "-T", "-p", strconv.Itoa(pid))
output, err := cmd.Output()
if err != nil {
return nil, err
}
lines := strings.Split(string(output), "\n")
var threads []string
for _, line := range lines[1:] {
if strings.TrimSpace(line) != "" {
threads = append(threads, line)
}
}
return threads, nil
}
func getIOStats(pid int) (string, error) {
filePath := fmt.Sprintf("/proc/%d/io", pid)
data, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(data), nil
}
func getProcessStartTime(pid int) (string, error) {
cmd := exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "lstart=")
output, err := cmd.Output()
if err != nil {
return "", err
}
return strings.TrimSpace(string(output)), nil
}
func getParentProcess(pid int) (int, error) {
cmd := exec.Command("ps", "-o", "ppid=", "-p", strconv.Itoa(pid))
output, err := cmd.Output()
if err != nil {
return 0, err
}
ppid, err := strconv.Atoi(strings.TrimSpace(string(output)))
if err != nil {
return 0, err
}
return ppid, nil
}
func getNetworkConnections(pid int) (string, error) {
cmd := exec.Command("lsof", "-p", strconv.Itoa(pid), "-i")
output, err := cmd.Output()
if err != nil {
return "", err
}
return string(output), nil
}

View File

@@ -1,6 +1,9 @@
package system
import "fmt"
import (
"fmt"
"time"
)
func ExampleSetOsEnv() {
err := SetOsEnv("foo", "abc")
@@ -75,3 +78,56 @@ func ExampleGetOsBits() {
// Output:
// 64
}
func ExampleStartProcess() {
pid, err := StartProcess("sleep", "2")
if err != nil {
return
}
fmt.Println(pid)
}
func ExampleStopProcess() {
pid, err := StartProcess("sleep", "10")
if err != nil {
return
}
time.Sleep(1 * time.Second)
err = StopProcess(pid)
fmt.Println(err)
// Output:
// <nil>
}
func ExampleKillProcess() {
pid, err := StartProcess("sleep", "3")
if err != nil {
return
}
time.Sleep(1 * time.Second)
err = KillProcess(pid)
fmt.Println(err)
// Output:
// <nil>
}
func ExampleGetProcessInfo() {
pid, err := StartProcess("ls", "-a")
if err != nil {
return
}
processInfo, err := GetProcessInfo(pid)
if err != nil {
return
}
fmt.Println(processInfo)
}

View File

@@ -1,8 +1,10 @@
package system
import (
"os/exec"
"strings"
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
@@ -53,7 +55,9 @@ func TestExecCommand(t *testing.T) {
assert := internal.NewAssert(t, "TestExecCommand")
// linux or mac
stdout, stderr, err := ExecCommand("ls")
stdout, stderr, err := ExecCommand("ls", func(cmd *exec.Cmd) {
cmd.Dir = "/"
})
t.Log("std out: ", stdout)
t.Log("std err: ", stderr)
assert.Equal("", stderr)
@@ -74,16 +78,6 @@ func TestExecCommand(t *testing.T) {
assert.IsNotNil(err)
}
// func TestExecCommandWithOption(t *testing.T) {
// assert := internal.NewAssert(t, "TestExecCommandWithOption")
// stdout, stderr, err := ExecCommand("ls", WithForeground())
// t.Log("std out: ", stdout)
// t.Log("std err: ", stderr)
// assert.Equal("", stderr)
// assert.IsNil(err)
// }
func TestGetOsBits(t *testing.T) {
t.Parallel()
@@ -95,3 +89,56 @@ func TestGetOsBits(t *testing.T) {
t.Error("os is not 32 or 64 bits")
}
}
func TestStartProcess(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestStartProcess")
pid, err := StartProcess("ls", "-a")
assert.IsNil(err)
assert.Equal(true, pid > 0)
}
func TestKillProcess(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestKillProcess")
pid, err := StartProcess("ls")
assert.IsNil(err)
assert.Equal(true, pid > 0)
err = KillProcess(pid)
assert.IsNil(err)
}
func TestStopProcess(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestStopProcess")
pid, err := StartProcess("ls")
assert.IsNil(err)
assert.Equal(true, pid > 0)
err = StopProcess(pid)
assert.IsNil(err)
}
func TestGetProcessInfo(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGetProcessInfo")
pid, err := StartProcess("ls", "-a")
assert.IsNil(err)
time.Sleep(1 * time.Second)
processInfo, err := GetProcessInfo(pid)
assert.IsNil(err)
t.Log(processInfo)
}