mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-14 17:52:28 +08:00
Compare commits
39 Commits
3e8c3bd396
...
v2.3.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d6ab72059 | ||
|
|
30eb2c72b0 | ||
|
|
adf18a2e47 | ||
|
|
f99a8ef3cf | ||
|
|
fcdf1d5839 | ||
|
|
ee0afed963 | ||
|
|
1d94896c9b | ||
|
|
69cf9bbcf0 | ||
|
|
2bbcb85286 | ||
|
|
e58c9b797b | ||
|
|
3e1ac5e0b5 | ||
|
|
8869e0440d | ||
|
|
69f9c74bcb | ||
|
|
84ebc7ce71 | ||
|
|
c745097749 | ||
|
|
ba75e58e5f | ||
|
|
7e85a0ed7d | ||
|
|
2268a0312f | ||
|
|
da84d95aa3 | ||
|
|
48244d6711 | ||
|
|
5e3337a52e | ||
|
|
c3372e18b1 | ||
|
|
90e5a0bfb2 | ||
|
|
93be25920f | ||
|
|
f9e5ec9096 | ||
|
|
601df5dc12 | ||
|
|
63216d9b1c | ||
|
|
c32a19868d | ||
|
|
71e914019b | ||
|
|
9824db0056 | ||
|
|
ba9188a29a | ||
|
|
8625fbd8d3 | ||
|
|
81b29baf30 | ||
|
|
5a38e34063 | ||
|
|
159168dd7b | ||
|
|
ec092a009a | ||
|
|
ca40b5d6c6 | ||
|
|
a6d39a3bba | ||
|
|
38148978cf |
169
README.md
169
README.md
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -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. <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. <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. <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. <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. <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. <a href="#index">index</a></h3>
|
||||
|
||||
|
||||
173
README_zh-CN.md
173
README_zh-CN.md
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -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 日期时间处理包,格式化日期,比较日期。 <a href="#index">回到目录</a></h3>
|
||||
<h3 id="datetime"> 7. datetime日期时间处理包,格式化日期,比较日期。 <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。 <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 包实现了一些数学计算的函数。 <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。 <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 实现,功能有限。 <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 的相关函数。 <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 包实现一个元组数据类型。 <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)]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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>',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
```
|
||||
@@ -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 day,keeps the doctor away
|
||||
// Output:
|
||||
// Hello World!
|
||||
// Go Language
|
||||
// An apple a day,keeps 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]
|
||||
}
|
||||
```
|
||||
@@ -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-a(linux),dir(windows),ping 127.0.0.1。在linux中,使用/bin/bash-c执行命令,在windows中,使用powershell.exe执行命令。</p>
|
||||
<p>执行shell命令,返回命令的stdout和stderr字符串,如果出现错误,则返回错误。参数`command`是一个完整的命令字符串,如ls-a(linux),dir(windows),ping 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)
|
||||
}
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
```
|
||||
@@ -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 day,keeps the doctor away
|
||||
}
|
||||
```
|
||||
|
||||
// Output:
|
||||
// Hello World!
|
||||
// Go Language
|
||||
// An apple a day,keeps 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]
|
||||
}
|
||||
```
|
||||
@@ -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)
|
||||
}
|
||||
```
|
||||
@@ -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,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
199
maputil/map.go
199
maputil/map.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
431
maputil/orderedmap.go
Normal 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
245
maputil/orderedmap_test.go
Normal 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"))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -694,3 +694,62 @@ func ExampleConcat() {
|
||||
// Go Language
|
||||
// An apple a day,keeps 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]
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
190
system/os.go
190
system/os.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user