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

Compare commits

..

175 Commits

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

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

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

---------

Co-authored-by: dongyue16 <dongyue16@tal.com>
2024-08-29 19:37:42 +08:00
dudaodong
ec092a009a test: we should write clean unit test code 2024-08-28 16:06:08 +08:00
dudaodong
ca40b5d6c6 refactoring: rename SortByKeys to SortByKey 2024-08-28 10:58:14 +08:00
dudaodong
a6d39a3bba feat: add OrderedMap for issue #237 2024-08-28 10:53:53 +08:00
dudaodong
38148978cf refactoring: change unit variable to package internal 2024-08-26 16:25:57 +08:00
dudaodong
3e8c3bd396 feat: add SortbyKeys for map 2024-08-23 11:21:29 +08:00
dudaodong
30971c1aab doc: rename CONTRIBUTION.md and add github start chart 2024-08-21 10:28:26 +08:00
dudaodong
bc260277bc fix: fix DecimalBytes and BinaryBytes issue #232 2024-08-19 11:36:49 +08:00
dudaodong
c0b200f846 feat: add ReduceConcurrent 2024-08-15 17:48:26 +08:00
dudaodong
305847993c feat: add ForEachConcurrent 2024-08-15 16:44:22 +08:00
dudaodong
f5d70728c3 refactoring: rename param and change its order 2024-08-15 15:50:48 +08:00
残念
c2a5335bc6 feat: add RandSliceFromGivenSlice function (#236) 2024-08-15 15:20:36 +08:00
残念
7b4e060f85 feat: add RandFromGivenSlice function (#235)
* feat: add RandFromGivenSlice function

* feat: add RandFromGivenSlice function
2024-08-14 19:36:12 +08:00
dudaodong
a360372aa9 feat: add FilterConcurrent 2024-08-14 11:19:10 +08:00
dudaodong
7f78a6b11e refactoring: rename slice_parallel to slice_concurrent 2024-08-14 10:52:36 +08:00
dudaodong
5c53cb5867 feat: add MapConcurrent 2024-08-14 10:45:35 +08:00
dudaodong
f7e9d5dc47 doc: add doc for UniqueByComparator and UniqueByParallel 2024-08-13 11:00:47 +08:00
dudaodong
5c580ed013 test: update test for Schedule 2024-08-09 15:00:19 +08:00
dudaodong
0f9764f41e feat: add Throttle function 2024-08-09 14:28:15 +08:00
dudaodong
0bc5f82554 doc: add RandBool, RandBoolSlice 2024-08-09 11:47:36 +08:00
dudaodong
e91965b013 feat: add RandStringSlice, RandBool, RandBoolSlice 2024-08-09 11:44:56 +08:00
dudaodong
483a286d8e feat: add RandIntSlice function 2024-08-09 10:46:08 +08:00
dudaodong
3f8e306ced doc: update deprecated warning text 2024-08-08 15:44:19 +08:00
dudaodong
0b29f0520d feat: add Debounce function 2024-08-08 15:19:38 +08:00
dudaodong
8611ec0c10 feat: add UniqueByComparator 2024-08-08 11:23:11 +08:00
dudaodong
286e10d189 refactoring: memory optimization for unique slice 2024-08-08 10:51:33 +08:00
dudaodong
3e7f94b03e fix: fix UniqueBy bug 2024-08-08 10:40:23 +08:00
dudaodong
356351896d feat: add UniqueByParallel for slice 2024-08-08 10:20:52 +08:00
dudaodong
9be124211e refact: preallocate in Merge map 2024-08-01 11:00:24 +08:00
dudaodong
f467658481 Merge branch 'rc' into v2 2024-07-31 10:50:29 +08:00
zyfx
5c9d0e396e Update condition.go (#234) 2024-07-31 10:16:03 +08:00
dudaodong
8f74460c1b fix: remove scientific notation in DecimalBytes 2024-07-30 14:17:17 +08:00
dudaodong
d5752499bf fix: remove scientific notation in DecimalBytes 2024-07-29 11:42:56 +08:00
dudaodong
eb7cf76eae doc: fix doc error 2024-07-18 11:44:00 +08:00
dudaodong
4af074d181 doc: add play ground demo 2024-07-18 11:34:16 +08:00
dudaodong
73fb8fefd2 release v2.3.2 2024-07-18 10:52:54 +08:00
dudaodong
9cf535055d doc: update readme file 2024-07-18 10:50:29 +08:00
dudaodong
8be7b3e396 rename UnwrapOr 2024-07-18 10:12:16 +08:00
梧桐
dac706d700 🐛 Fixing a bug. about pointer package function (#230)
update:
1. UnwarpOr
2. UnwarpOrDefault
3. ExtractPointer

add:
1. UnwrapOr
2. IsNil
2024-07-18 10:05:07 +08:00
dudaodong
2097277a7d doc: add doc for UniqueByField 2024-06-25 15:20:03 +08:00
dudaodong
95b516e278 feat: add UniqueByField 2024-06-24 19:36:02 +08:00
dudaodong
ca373b00a7 feat: add GetOrSet 2024-06-24 17:29:12 +08:00
dudaodong
a220220f09 feat: add GetOrSet 2024-06-24 17:28:51 +08:00
dudaodong
aeef0418a4 fix: fix bug of CopyDir 2024-06-24 17:09:31 +08:00
dudaodong
9b7d8d7abf doc: update vitepress version 2024-06-11 10:06:33 +08:00
dudaodong
4d21e81263 feat: add Timeout config for http client 2024-06-04 16:28:00 +08:00
残念
ce2397422e perf(slice): make a clearer panic description (#223) 2024-05-30 16:55:30 +08:00
Cannian
4b3a62b36a perf(validator): check Ipv4、Ipv6 by more graceful method (#220) 2024-05-25 08:58:33 +08:00
dudaodong
e054680d20 doc: ignoreDeadLinks in doc 2024-05-14 11:42:48 +08:00
dudaodong
5381842eec doc: update doc for v2.3.1 2024-05-14 11:26:14 +08:00
dudaodong
6e0498514c doc: update doc for v2.3.1 2024-05-14 11:25:01 +08:00
dudaodong
967e6a3493 doc: update doc for v2.3.1 2024-05-14 10:47:27 +08:00
dudaodong
5b24801e49 merge qa branch 2024-05-14 10:11:29 +08:00
dudaodong
974ba525a6 Merge branch 'rc' into v2 2024-05-14 10:10:45 +08:00
chentong
f0235c40b6 fix: json tag omitempty convert error (#218) 2024-05-14 10:08:56 +08:00
dudaodong
712a215ea6 reset 2024-05-14 10:02:56 +08:00
dudaodong
7893f828d3 fix: fix get tag failed 2024-05-13 17:49:34 +08:00
Yang Li
53fa210f09 refactor slice.Unique() (#215) 2024-05-09 10:43:59 +08:00
dudaodong
de9ee08be4 test: update net error handle 2024-04-18 14:23:36 +08:00
dudaodong
5381450bea feat: fix email validation failed 2024-04-18 14:18:33 +08:00
Cannian
6853d627f4 refactor(slice): optimize function (#211) 2024-04-06 09:16:28 +08:00
Joker-desire
e461acdb72 fix(netutil): Add proxy IP to send request (#210)
* fix(netutil): Add proxy ip to send request

* fix(netutil): Add proxy IP to send request

---------

Co-authored-by: 杨崟 <yangyin@addcn.com>
2024-04-03 16:52:53 +08:00
dudaodong
2a796adf85 fix: support nest struct in StructToUrlValues 2024-04-02 17:38:40 +08:00
Cannian
5e6e8d82a8 feat(maputil): add ToSortedSlicesDefault method and ToSortedSlicesWithComparator method (#206) 2024-03-31 21:04:55 +08:00
Cannian
e9280b8c25 add Concat method (#204)
* feat(strutil): add Concat method

* feat(strutil): add Concat method
2024-03-25 10:26:37 +08:00
dudaodong
bb6f10a1fb rename CONTRIBUTING file 2024-03-17 10:30:24 +08:00
dudaodong
33b4cffe60 rename CONTRIBUTING file 2024-03-17 10:30:07 +08:00
Cannian
2b765b49e0 refactor(set): pop method randomly removes an element and return (#202) 2024-03-17 10:28:25 +08:00
dudaodong
004dbdc32e doc: update doc for RightPadding and LeftPadding 2024-03-12 09:52:02 +08:00
donutloop
ab50e8120a Slice: padding (#201)
* LeftPadding adds padding to the left begin of a slice.
* RightPadding adds padding to the right end of a slice.
2024-03-10 21:24:17 +08:00
dudaodong
73c97af7d8 doc: update doc for Break function 2024-03-08 21:39:06 +08:00
donutloop
5e8a065eaa Slice: break (#200)
Splits a slice into two based on a predicate function. It starts appending to the second slice after the first element that matches the predicate. All elements after the first match are included in the second slice, regardless of whether they match the predicate or not.
2024-03-07 14:11:19 +08:00
dudaodong
aa74400607 doc: fix doc text error 2024-03-06 16:19:20 +08:00
dudaodong
a6eaaef563 doc: add go playground demo 2024-03-06 15:28:55 +08:00
dudaodong
1b31014f81 doc: add go playground demo 2024-03-06 14:52:35 +08:00
dudaodong
036847577d release v2.3.0 2024-03-05 14:57:16 +08:00
dudaodong
d21edd1cde doc: add doc for retry package 2024-03-05 14:38:42 +08:00
dudaodong
c58c50327c doc: add doc for ChunkRead and ParallelChunkRead 2024-03-05 11:42:03 +08:00
dudaodong
9bfdc686f8 doc: update contribution_guide file 2024-03-05 10:47:51 +08:00
dudaodong
5ca8f6ef6f doc: update doc 2024-03-05 10:45:40 +08:00
dudaodong
a54d4c79a0 doc: update doc for mathutil package 2024-03-04 19:52:49 +08:00
Cannian
f7b54986aa feat(mathutil): add DIv, FloorToFloat, FloorToString, CeilToFloat, CeilToString (#199) 2024-03-04 17:40:31 +08:00
dudaodong
92fae4273b doc: update doc for new added functions 2024-03-04 16:42:59 +08:00
dudaodong
0d29f5437a doc: update doc for new added functions 2024-03-04 16:41:54 +08:00
dudaodong
e95d7c82cd doc: update doc for function package 2024-03-04 10:19:39 +08:00
donutloop
e138043289 Function: AcceptIf (#198)
AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
A predicate function that takes an argument of type T and returns a bool.
An apply function that also takes an argument of type T and returns a modified value of the same type.
2024-03-04 10:00:43 +08:00
dudaodong
aabfcb7bde doc: update doc for HammingDistance 2024-03-03 21:55:27 +08:00
donutloop
0b5e884371 Strutil: HammingDistance func (#197)
* Strutil: HammingDistance func

The Hamming distance is the number of positions at which the corresponding symbols are different

* Add hamming distance doc
2024-03-03 21:48:34 +08:00
dudaodong
3d1bd08434 doc: update doc for new functions 2024-03-01 22:38:57 +08:00
dudaodong
a62ad71791 Merge branch 'rc' into v2 2024-03-01 21:45:08 +08:00
Cannian
c02c4f813b refactor(mathutil): round related method support generics (#195) 2024-03-01 16:04:23 +08:00
pigwantacat
235d2f2486 修复MAXIMUM_CAPACITY超过int最大值 (#196)
* refactor:refactor random function

* fix:fix random function

---------

Co-authored-by: zhuhebin <zhuhebin@fengtaisec.com>
2024-03-01 15:52:49 +08:00
dudaodong
e9380a3d9f fix: fix unused parameters vet issue 2024-03-01 10:05:28 +08:00
Cannian
81d13c2f1a feat(convertor): add ToBase64 related method (#194) 2024-02-29 19:22:32 +08:00
dudaodong
7290296849 update Contributing Guide 2024-02-29 11:49:34 +08:00
dudaodong
8a8460a592 Merge branch 'rc' into v2 2024-02-29 11:34:53 +08:00
dudaodong
7a98c431d3 Merge branch 'main' of github.com:duke-git/lancet 2024-02-29 11:32:44 +08:00
colorcrow
606d887230 增加ParallelChunkRead方法,分块并发读取超大文本 (#192) 2024-02-29 11:32:10 +08:00
donutloop
473f9c9f3e Iterator: general refactoring and reset method (#193)
Feature

Reset allows for the iteration process over a sequence to be restarted from the beginning.
It enables reusing the iterator for multiple traversals without needing to recreate it.

Refactoring

It is a idiomatic practice to design functions and methods to return concrete struct types.
This approach promotes flexibility and decoupling, allowing the calling code to work with any implementation that satisfies the interface
2024-02-29 11:30:12 +08:00
dudaodong
9ff3d0e79c create rc branch for release candidate 2024-02-29 11:25:20 +08:00
dudaodong
5db1d07d6d Merge branch 'main' into v2 2024-02-28 15:55:59 +08:00
Cannian
6c6d14828a feat(set, doc): add ToSlice,ToSortedSlice method,fix doc (#189) 2024-02-28 15:43:34 +08:00
donutloop
0e1593c67b Slice: Add SetToDefaultIf (#187)
SetToDefaultIf sets elements to their zero value if they match the given predicate.
It retains the positions of the elements in the slice.
It returns slice of T and the count of modified slice items
2024-02-28 11:27:23 +08:00
cannian1
6c7f38d8b3 feat: more readable panic (#183) 2024-02-27 10:38:08 +08:00
donutloop
069812e0ee Hashmap: Add FilterByValue (#184)
The FilterByValue function is a method defined on the HashMap type. It generates a new HashMap
containing only the elements that satisfy a specified condition,
as determined by a predicate function applied to each element's value.

Note: Will add later doc
2024-02-27 10:36:26 +08:00
dudaodong
4a539a23c8 doc: add doc for Xnor and Nand in function package 2024-02-26 10:10:37 +08:00
dudaodong
0b1dab0399 Merge branch 'main' into v2 2024-02-26 10:02:00 +08:00
donutloop
805e2543d0 Add functional predicate NAND (#182)
Add new function, NAND, designed to create a composed predicate representing the logical NAND operation
applied to a list of predicates. The NAND operation is a logical operation
that returns true only if all perdicate result in false otherwise false
2024-02-26 09:58:19 +08:00
dudaodong
a3d518da76 remove test_src directory 2024-02-25 20:29:49 +08:00
dudaodong
e3e2d8394c Merge branch 'v2' of github.com:duke-git/lancet into v2 2024-02-25 20:25:44 +08:00
yunxuan
0eeaa06055 feat(fileutil): add CopyDir func (#180)
* add fileutil.CopyDir

* remove debug code
2024-02-25 20:25:00 +08:00
donutloop
a43bc554ee Add functional predicate XNOR (#181)
Add new function, Xnor, designed to create a composed predicate representing
the logical Exclusive NOR (XNOR) operation applied to a list of predicates.
The XNOR operation is a logical operation that returns true only
if all operands have the same boolean value
2024-02-25 20:24:47 +08:00
dudaodong
aebab7c944 refactor: break change, rename constructor of set (NewSet->New, NewSetFromSlice->FromSlice) 2024-02-25 09:32:32 +08:00
dudaodong
665bad4ca3 doc: update doc for IndexOfFunc and LastIndexOfFunc 2024-02-25 09:25:07 +08:00
donutloop
e4901e99e9 Fix optional doc links (#179) 2024-02-24 18:12:44 +08:00
donutloop
4277e8eca5 CopyOnWriteList add IndexOfFunc and LastIndexOfFunc (#178)
Allow users to apply functional predicates alongside 'index of' and 'last index of' search methods in this specialized list variant
2024-02-24 17:53:58 +08:00
donutloop
fdc93c8cc7 Change naming (#177)
Utilize terminology from the Go SDK rather than introducing novel terms to describe concepts.
2024-02-24 17:25:31 +08:00
donutloop
860a499f98 Add custom backoff setter (#176)
Users should have the capability to customize the backoff pattern and accordingly adjust it within the retry mechanism.
2024-02-23 10:04:38 +08:00
dudaodong
2e1c2276a5 test: add test coverageg 2024-02-22 14:39:53 +08:00
donutloop
d367397dab Add exponential With jitter backoff (#174)
* Add exponential With jitter backoff

Adds exponential + jitter retry policy. To enable drastic slow down of sending out requests to any external system.

Jitter in computational contexts refers to the addition of a small random variation to a value
to break the symmetric patterns

* Retry with exp: Allow shift for all multiple of 2
2024-02-22 10:39:45 +08:00
dudaodong
66fd8cf651 fix: fix go vet issue 2024-02-21 11:14:19 +08:00
dudaodong
a6be1828b9 doc: add doc and example for predicate logic of function package 2024-02-21 11:13:47 +08:00
dudaodong
8f5d297572 . 2024-02-21 11:11:52 +08:00
dudaodong
a1a4fdc598 doc: update doc for optional 2024-02-21 10:38:26 +08:00
dudaodong
1610076d22 Merge branch 'main' into v2 2024-02-21 10:21:42 +08:00
donutloop
cacbf97223 Add Retry backoff policy (#173)
The aim of this policy is to enable the configuration of various types of backoff mathematical curves. Should this modification be deemed suitable,
I will proceed to implement an exponential curve backoff policy and set of custom backoff policy
Warning: It's major break
2024-02-21 10:20:24 +08:00
donutloop
cd156dba5f Add functional nor predicate (#172) 2024-02-21 10:05:54 +08:00
dudaodong
3a71a8697d fix: fix issue #169 2024-02-20 17:29:32 +08:00
dudaodong
c88fd3db86 fix: fix go vet issue, method Unwrap() []error 2024-02-20 11:41:58 +08:00
dudaodong
27d19d1717 fix: rename Seek to SeekOffset fix go vet check issue 2024-02-20 11:39:41 +08:00
dudaodong
da24bae6b4 doc: add doc for Optional type 2024-02-20 11:22:39 +08:00
donutloop
3cd9d6b68c Add functional predicate (#171)
Enable the execution of assertion functions in a functional manner.
2024-02-20 09:55:39 +08:00
dudaodong
874d09f331 refactor: make stream struct exported 2024-02-19 15:55:08 +08:00
dudaodong
fdf251ac98 add govet check to github action file 2024-02-19 15:50:19 +08:00
dudaodong
7ec2533b7a feat: add MapToStruct 2024-02-19 13:50:06 +08:00
dudaodong
9fd0603f4a fix: fix copylocks warning in Optional struct methods 2024-02-19 10:22:28 +08:00
dudaodong
9f7b416a8d Merge branch 'main' into v2 2024-02-19 10:00:21 +08:00
donutloop
bf4b2b5fd6 Add optional (#170)
* Add optional

Wrapper container with easy to understand helper methods

* Add test and rewrite test

* Add panic test

* Add TestOrElseGetHappyPath
2024-02-19 09:59:42 +08:00
dudaodong
22af59565e fix: fix issue #168 2024-02-06 16:59:01 +08:00
dudaodong
f9e047f190 feat: add SubInBetween 2024-02-06 16:47:30 +08:00
dudaodong
fa298b740d add playground demo 2024-02-01 10:41:09 +08:00
126 changed files with 17578 additions and 1904 deletions

View File

@@ -3,11 +3,11 @@ on:
push:
branches:
- main
# - v2
- rc
pull_request:
branches:
- main
# - v2
- rc
jobs:
build:
runs-on: ubuntu-latest
@@ -17,8 +17,10 @@ jobs:
fetch-depth: 2
- uses: actions/setup-go@v2
with:
go-version: "1.18"
go-version: "1.20"
- name: Run coverage
run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic
- name: Run govet
run: go vet -v ./...
- name: Upload coverage to Codecov
run: bash <(curl -s https://codecov.io/bash)

View File

@@ -1,4 +1,4 @@
# Lancet Contributing Guide
# Lancet Contribution Guide
Hi! Thank you for choosing Lancet.
@@ -30,7 +30,7 @@ We are excited that you are interested in contributing to lancet. Before submitt
- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass.
- Make sure PRs are created to `v2` branch instead of `master` branch.
- Make sure PRs are created to `rc` branch instead of other branch.
- If your PR fixes a bug, please provide a description about the related bug.

View File

@@ -30,7 +30,7 @@ Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代
- 提交 PR 前请执行单元测试命令go test -v ./...,确保所有单元测试任务通过。
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
- 确保 PR 是提交到 `rc` 分支,而不是其他分支。
- 如果是修复 bug请在 PR 中给出描述信息。

292
README.md
View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.2.9-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.3-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -24,7 +24,7 @@
## Features
- 👏 Comprehensive, efficient and reusable.
- 💪 600+ go util functions, support string, slice, datetime, net, crypt...
- 💪 700+ go util functions, support string, slice, datetime, net, crypt...
- 💅 Only depends on two kinds of libraries: go standard library and golang.org/x.
- 🌍 Unit test for every exported function.
@@ -38,7 +38,7 @@
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
```
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.2. </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
@@ -318,6 +318,14 @@ import "github.com/duke-git/lancet/v2/convertor"
- **<big>GbkToUtf8</big>** : converts GBK encoding data to utf8 encoding data.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#GbkToUtf8)]
[[play](https://go.dev/play/p/OphmHCN_9u8)]
- **<big>ToStdBase64</big>** : converts a value to a string encoded in standard Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToStdBase64)]
- **<big>ToUrlBase64</big>** : converts a value to a string encoded in url Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToUrlBase64)]
- **<big>ToRawStdBase64</big>** : converts a value to a string encoded in raw standard Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawStdBase64)]
- **<big>ToRawUrlBase64</big>** : converts a value to a string encoded in raw url Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawUrlBase64)]
<h3 id="cryptor"> 6. Cryptor package is for data encryption and decryption.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -354,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)]
@@ -595,6 +609,16 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>TimestampNano</big>** : returns current nano second timestamp.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TimestampNano)]
[[play](https://go.dev/play/p/A9Oq_COrcCF)]
- **<big>TrackFuncTime</big>** : tracks function execution time.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TrackFuncTime)]
[[play](todo)]
- **<big>DaysBetween</big>** : returns the number of days between two times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#DaysBetween)]
[[play](todo)]
- **<big>GenerateDatetimesBetween</big>** : returns a slice of strings between two times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GenerateDatetimesBetween)]
[[play](todo)]
<h3 id="datastructure"> 8. Datastructure package contains some common data structure. eg. list, linklist, stack, queue, set, tree, graph. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -608,6 +632,7 @@ import set "github.com/duke-git/lancet/v2/datastructure/set"
import tree "github.com/duke-git/lancet/v2/datastructure/tree"
import heap "github.com/duke-git/lancet/v2/datastructure/heap"
import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
import optional "github.com/duke-git/lancet/v2/datastructure/optional"
```
#### Structure list:
@@ -630,6 +655,9 @@ import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/heap.md)]
- **<big>Hashmap</big>** : hash map structure.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/hashmap.md)]
- **<big>Optional</big>** : Optional container.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/optional.md)]
<h3 id="fileutil"> 9. Fileutil package implements some basic functions for file operations. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -648,9 +676,12 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>CreateDir</big>** : create directory in absolute path.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CreateDir)]
[[play](https://go.dev/play/p/qUuCe1OGQnM)]
- **<big>CopyFile</big>** :copy src file to dest file.
- **<big>CopyFile</big>** : copy src file to dest file.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CopyFile)]
[[play](https://go.dev/play/p/Jg9AMJMLrJi)]
- **<big>CopyDir</big>** : copy src directory to dest directory.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CopyDir)]
[[play](https://go.dev/play/p/YAyFTA_UuPb)]
- **<big>FileMode</big>** : return file's mode and permission.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#FileMode)]
[[play](https://go.dev/play/p/2l2hI42fA3p)]
@@ -708,7 +739,7 @@ import "github.com/duke-git/lancet/v2/fileutil"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteCsvFile)]
- **<big>WriteMapsToCsv</big>** : write slice of map to csv file.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteMapsToCsv)]
[[play](https://go.dev/play/p/dAXm58Q5U1o)]
[[play](https://go.dev/play/p/umAIomZFV1c)]
- **<big>WriteBytesToFile</big>** : write bytes to target file.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteBytesToFile)]
[[play](https://go.dev/play/p/s7QlDxMj3P8)]
@@ -717,6 +748,12 @@ import "github.com/duke-git/lancet/v2/fileutil"
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
- **<big>ReadFile</big>** : read file or url.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ReadFile)]
- **<big>ChunkRead</big>** : reads a block from the file at the specified offset and returns all lines within the block.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ChunkRead)]
[[play](https://go.dev/play/p/r0hPmKWhsgf)]
- **<big>ParallelChunkRead</big>** : reads the file in parallel and send each chunk of lines to the specified channel.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ParallelChunkRead)]
[[play](https://go.dev/play/p/teMXnCsdSEw)]
<h3 id="formatter"> 10. Formatter contains some functions for data formatting. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -726,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.
@@ -771,19 +808,47 @@ 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)]
- **<big>Pipeline</big>** : takes a list of functions and returns a function whose param will be passed into the functions one by one.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Pipeline)]
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
- **<big>AcceptIf</big>** : returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#AcceptIf)]
[[play](https://go.dev/play/p/XlXHHtzCf7d)]
- **<big>And</big>** : returns a composed predicate that represents the logical AND of a list of predicates.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#And)]
[[play](https://go.dev/play/p/dTBHJMQ0zD2)]
- **<big>Or</big>** : returns a composed predicate that represents the logical OR of a list of predicates.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Or)]
[[play](https://go.dev/play/p/LitCIsDFNDA)]
- **<big>Negate</big>** : returns a predicate that represents the logical negation of this predicate.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Negate)]
[[play](https://go.dev/play/p/jbI8BtgFnVE)]
- **<big>Nor</big>** : returns a composed predicate that represents the logical NOR of a list of predicates.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Nor)]
[[play](https://go.dev/play/p/2KdCoBEOq84)]
- **<big>Nand</big>** : returns a composed predicate that represents the logical Nand of a list of predicates.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Nand)]
[[play](https://go.dev/play/p/Rb-FdNGpgSO)]
- **<big>Xnor</big>** : returns a composed predicate that represents the logical XNOR of a list of predicates.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Xnor)]
[[play](https://go.dev/play/p/FJxko8SFbqc)]
- **<big>Watcher</big>** : Watcher is used for record code execution time. can start/stop/reset the watch timer. get the elapsed time of function execution.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Watcher)]
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
<h3 id="maputil"> 12. Maputil package includes some functions to manipulate map.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
@@ -858,6 +923,72 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>HasKey</big>** : checks if map has key or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#HasKey)]
[[play](https://go.dev/play/p/isZZHOsDhFc)]
- **<big>GetOrSet</big>** : returns value of the given key or set the given value value if not present.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#GetOrSet)]
[[play](https://go.dev/play/p/IVQwO1OkEJC)]
- **<big>MapToStruct</big>** : converts map to struct.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapToStruct)]
[[play](https://go.dev/play/p/7wYyVfX38Dp)]
- **<big>ToSortedSlicesDefault</big>** : converts a map to two slices sorted by key: one for the keys and another for the values.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesDefault)]
[[play](https://go.dev/play/p/43gEM2po-qy)]
- **<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)]
@@ -882,6 +1013,13 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ConcurrentMap_Range</big>** : calls iterator sequentially for each key and value present in each of the shards in the map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ConcurrentMap_Range)]
[[play](https://go.dev/play/p/iqcy7P8P0Pr)]
- **<big>SortByKey</big>** : sorts the map by its keys and returns a new map with sorted keys.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#SortByKey)]
[[play](todo)]
- **<big>GetOrDefault</big>** : returns the value of the given key or a default value if the key is not present.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#GetOrDefault)]
[[play](todo)]
<h3 id="mathutil"> 13. Mathutil package implements some functions for math calculation. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -927,6 +1065,18 @@ import "github.com/duke-git/lancet/v2/mathutil"
- **<big>TruncRound</big>** : round off n decimal places for int64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#TruncRound)]
[[play](https://go.dev/play/p/aumarSHIGzP)]
- **<big>CeilToFloat</big>** : round float up n decimal places.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#CeilToFloat)]
[[play](https://go.dev/play/p/8hOeSADZPCo)]
- **<big>CeilToString</big>** : round float up n decimal places, then conver to string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#CeilToString)]
[[play](https://go.dev/play/p/wy5bYEyUKKG)]
- **<big>FloorToFloat</big>** : round float down n decimal places.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#FloorToFloat)]
[[play](https://go.dev/play/p/vbCBrQHZEED)]
- **<big>FloorToString</big>** : round float down n decimal places, then conver to string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#FloorToString)]
[[play](https://go.dev/play/p/Qk9KPd2IdDb)]
- **<big>Range</big>** : Creates a slice of numbers from start with specified count, element step is 1.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Range)]
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
@@ -966,6 +1116,10 @@ import "github.com/duke-git/lancet/v2/mathutil"
- **<big>Abs</big>** : returns the absolute value of param number.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)]
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
- **<big>Div</big>** : returns the result of x divided by y.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Div)]
[[play](https://go.dev/play/p/WLxDdGXXYat)]
<h3 id="netutil"> 14. Netutil package contains functions to get net information and send http request. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1057,7 +1211,7 @@ import "github.com/duke-git/lancet/v2/pointer"
- **<big>Unwrap</big>** : return the value from the pointer.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#Unwrap)]
[[play](https://go.dev/play/p/cgeu3g7cjWb)]
- **<big>UnwarpOr</big>** : UnwarpOr returns the value from the pointer or fallback if the pointer is nil.
- **<big>UnwrapOr</big>** : UnwrapOr returns the value from the pointer or fallback if the pointer is nil.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#UnwrapOr)]
[[play](https://go.dev/play/p/mmNaLC38W8C)]
- **<big>UnwarpOrDefault</big>** : UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
@@ -1096,20 +1250,36 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>UUIdV4</big>** : generate a random UUID of version 4 according to RFC 4122.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#UUIdV4)]
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
- **<big>RandUniqueIntSlice</big>** : generate a slice of random int of length n that do not repeat.
- **<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 of length n that do not repeat.
- **<big>RandFloats</big>** : generate a slice of random float64 numbers that do not repeat.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloats)]
[[play](https://go.dev/play/p/I3yndUQ-rhh)]
- **<big>RandStringSlice</big>** : generate a slice of random string of length strLen based on charset.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandStringSlice)]
[[play](todo)]
- **<big>RandBool</big>** : generate a random boolean value (true or false).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandBool)]
[[play](todo)]
- **<big>RandBoolSlice</big>** : generate a random boolean slice of specified length.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandBoolSlice)]
[[play](todo)]
- **<big>RandIntSlice</big>** : generate a slice of random int. Number range in [min, max)
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandIntSlice)]
[[play](todo)]
- **<big>RandFromGivenSlice</big>** : generate a random element from given slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFromGivenSlice)]
[[play](todo)]
- **<big>RandSliceFromGivenSlice</big>** : generate a random slice of length num from given slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandSliceFromGivenSlice)]
[[play](todo)]
<h3 id="retry"> 17. Retry package is for executing a function repeatedly until it was successful or canceled by the context. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1135,6 +1305,18 @@ import "github.com/duke-git/lancet/v2/retry"
- **<big>RetryTimes</big>** : set times of retry.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryTimes)]
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
- **<big>BackoffStrategy</big>** : An interface that defines a method for calculating backoff intervals.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#BackoffStrategy)]
- **<big>RetryWithCustomBackoff</big>** : set abitary custom backoff strategy.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithCustomBackoff)]
[[play](https://go.dev/play/p/jIm_o2vb5Y4)]
- **<big>RetryWithLinearBackoff</big>** : set linear strategy backoff.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithLinearBackoff)]
[[play](https://go.dev/play/p/PDet2ZQZwcB)]
- **<big>RetryWithExponentialWithJitterBackoff</big>** : set exponential strategy backoff.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
[[play](https://go.dev/play/p/xp1avQmn16X)]
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1182,8 +1364,10 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/v2U2deugKuV)]
- **<big>DeleteAt</big>** : delete the element of slice at index.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteAt)]
[[play](https://go.dev/play/p/800B1dPBYyd)]
- **<big>DeleteRange</big>** : delete the element of slice from start index to end indexexclude).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteRange)]
[[play](https://go.dev/play/p/945HwiNrnle)]
- **<big>Drop</big>** : drop n elements from the start of a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Drop)]
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
@@ -1238,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)]
@@ -1265,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)]
@@ -1280,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)]
@@ -1331,9 +1524,18 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Unique</big>** : remove duplicate elements in slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Unique)]
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
- **<big>UniqueBy</big>** : call iteratee func with every item of slice, then remove duplicated.
- **<big>UniqueBy</big>** : remove duplicate elements from the input slice based on the values returned by the iteratee function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueBy)]
[[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>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)]
@@ -1358,6 +1560,20 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Random</big>** : get a random item of slice, return its index, when slice is empty, return -1.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Random)]
[[play](https://go.dev/play/p/UzpGQptWppw)]
- **<big>SetToDefaultIf</big>** : set elements to their default value if they match the given predicate.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#SetToDefaultIf)]
[[play](https://go.dev/play/p/9AXGlPRC0-A)]
- **<big>Break</big>** : breaks a list into two parts at the point where the predicate for the first time is true.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Break)]
- **<big>RightPadding</big>** : adds padding to the right end of a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#RightPadding)]
[[play](https://go.dev/play/p/0_2rlLEMBXL)]
- **<big>LeftPadding</big>** : adds padding to the left begin of a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#LeftPadding)]
[[play](https://go.dev/play/p/jlQVoelLl2k)]
- **<big>Frequency</big>** : counts the frequency of each element in the slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Frequency)]
[[play](todo)]
<h3 id="stream"> 19. Stream package implements a sequence of elements supporting sequential and operations. this package is an experiment to explore if stream in go can work as the way java does. its function is very limited. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1392,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)]
@@ -1603,7 +1822,27 @@ import "github.com/duke-git/lancet/v2/strutil"
- **<big>RemoveWhiteSpace</big>** : remove whitespace characters from a string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#RemoveWhiteSpace)]
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
- **<big>SubInBetween</big>** : return substring between the start and end position(excluded) of source string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#SubInBetween)]
[[play](https://go.dev/play/p/EDbaRvjeNsv)]
- **<big>HammingDistance</big>** : calculates the Hamming distance between two strings.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#HammingDistance)]
[[play](https://go.dev/play/p/glNdQEA9HUi)]
- **<big>Concat</big>** : concatenates strings.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Concat)]
[[play](todo)]
- **<big>Ellipsis</big>** : truncates a string to a specified length and appends an ellipsis.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Ellipsis)]
[[play](todo)]
- **<big>Shuffle</big>** : shuffle the order of characters of given string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Shuffle)]
[[play](todo)]
- **<big>Rotate</big>** : rotates the string by the specified number of characters.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Rotate)]
[[play](todo)]
- **<big>TemplateReplace</big>** : replaces the placeholders in the template string with the corresponding values in the data map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#TemplateReplace)]
[[play](todo)]
<h3 id="system"> 22. System package contain some functions about os, runtime, shell command. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1640,6 +1879,19 @@ import "github.com/duke-git/lancet/v2/system"
- **<big>GetOsBits</big>** : return current os bits (32 or 64).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetOsBits)]
[[play](https://go.dev/play/p/ml-_XH3gJbW)]
- **<big>StartProcess</big>** : start a new process with the specified name and arguments.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#StartProcess)]
[[play](todo)]
- **<big>StopProcess</big>** : stop a process by pid.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#StopProcess)]
[[play](todo)]
- **<big>KillProcess</big>** : kill a new process with the specified name and arguments.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#KillProcess)]
[[play](todo)]
- **<big>GetProcessInfo</big>** : retrieves detailed process information by pid.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetProcessInfo)]
[[play](todo)]
<h3 id="tuple"> 23. Tuple package implements tuple data type and some operations on it. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1943,11 +2195,15 @@ import "github.com/duke-git/lancet/v2/xerror"
## How to Contribute
#### [Contributing Guide](./CONTRIBUTING.en-US.md)
#### [Contribution Guide](./CONTRIBUTION.md)
## Contributors
Thank you to all the people who contributed to lancet!
<a href="https://github.com/duke-git/lancet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
</a>
</a>
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=duke-git/lancet&type=Date)](https://star-history.com/#duke-git/lancet&Date)

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.2.9-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.3-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -23,7 +23,7 @@
## 特性
- 👏 全面、高效、可复用。
- 💪 600+常用 go 工具函数,支持 string、slice、datetime、net、crypt...
- 💪 700+常用 go 工具函数,支持 string、slice、datetime、net、crypt...
- 💅 只依赖 go 标准库和 golang.org/x。
- 🌍 所有导出函数单元测试覆盖率 100%。
@@ -37,7 +37,7 @@
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
```
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.2。</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版本
@@ -100,6 +100,7 @@ func main() {
- [Validator](#user-content-validator)
- [Xerror](#user-content-xerror)
<h3 id="algorithm"> 1. algorithm 包实现一些基本查找和排序算法。 &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
@@ -317,6 +318,15 @@ import "github.com/duke-git/lancet/v2/convertor"
- **<big>GbkToUtf8</big>** : GBK 编码转 utf8 编码。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#GbkToUtf8)]
[[play](https://go.dev/play/p/OphmHCN_9u8)]
- **<big>ToStdBase64</big>** : 将值转换为StdBase64编码的字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToStdBase64)]
- **<big>ToUrlBase64</big>** : 将值转换为url Base64编码的字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToUrlBase64)]
- **<big>ToRawStdBase64</big>** : 将值转换为RawStdBase64编码的字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawStdBase64)]
- **<big>ToRawUrlBase64</big>** : 将值转换为RawUrlBase64编码的字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawUrlBase64)]
<h3 id="cryptor"> 6. cryptor 加密包支持数据加密和解密,获取 md5hash 值。支持 base64, md5, hmac, aes, des, rsa。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -353,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)]
@@ -462,7 +478,7 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/sSVmkfENKMz)]
<h3 id="datetime"> 7. datetime 日期时间处理包,格式化日期,比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="datetime"> 7. datetime日期时间处理包格式化日期比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/datetime"
@@ -596,6 +612,16 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>TimestampNano</big>** : 返回当前纳秒级时间戳。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#TimestampNano)]
[[play](https://go.dev/play/p/A9Oq_COrcCF)]
- **<big>TrackFuncTime</big>** : 测试函数执行时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#TrackFuncTime)]
[[play](todo)]
- **<big>DaysBetween</big>** : 返回两个日期之间的天数差。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#DaysBetween)]
[[play](todo)]
- **<big>GenerateDatetimesBetween</big>** : 生成从start到end的所有日期时间的字符串列表。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GenerateDatetimesBetween)]
[[play](todo)]
<h3 id="datastructure"> 8. datastructure 包含一些普通的数据结构实现。例如list, linklist, stack, queue, set, tree, graph。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -649,9 +675,12 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>CreateDir</big>** : 创建嵌套目录,例如/a/, /a/b/。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CreateDir)]
[[play](https://go.dev/play/p/qUuCe1OGQnM)]
- **<big>CopyFile</big>** :拷贝文件,会覆盖原有的文件。
- **<big>CopyFile</big>** : 拷贝文件,会覆盖原有的文件。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CopyFile)]
[[play](https://go.dev/play/p/Jg9AMJMLrJi)]
- **<big>CopyDir</big>** : 拷贝目录。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CopyDir)]
[[play](https://go.dev/play/p/YAyFTA_UuPb)]
- **<big>FileMode</big>** : 获取文件 mode 信息。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#FileMode)]
[[play](https://go.dev/play/p/2l2hI42fA3p)]
@@ -709,7 +738,7 @@ import "github.com/duke-git/lancet/v2/fileutil"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteCsvFile)]
- **<big>WriteMapsToCsv</big>** : 将map切片写入csv文件中。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteMapsToCsv)]
[[play](https://go.dev/play/p/dAXm58Q5U1o)]
[[play](https://go.dev/play/p/umAIomZFV1c)]
- **<big>WriteBytesToFile</big>** : 将 bytes 写入文件。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteBytesToFile)]
[[play](https://go.dev/play/p/s7QlDxMj3P8)]
@@ -718,6 +747,14 @@ import "github.com/duke-git/lancet/v2/fileutil"
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
- **<big>ReadFile</big>** : 读取文件或者URL。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFile)]
- **<big>ChunkRead</big>** : 从文件的指定偏移读取块并返回块内所有行。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ChunkRead)]
[[play](https://go.dev/play/p/r0hPmKWhsgf)]
- **<big>ParallelChunkRead</big>** : 并行读取文件并将每个块的行发送到指定通道。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ParallelChunkRead)]
[[play](https://go.dev/play/p/teMXnCsdSEw)]
<h3 id="formatter"> 10. formatter 格式化器包含一些数据格式化处理方法。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -727,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 字符串。
@@ -772,19 +809,48 @@ 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)]
- **<big>Pipeline</big>** : 从右至左执行函数列表。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Pipeline)]
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
- **<big>AcceptIf</big>** : AcceptIf函数会返回另一个函数该函数的签名与apply函数相同但同时还会包含一个布尔值来表示成功或失败。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#AcceptIf)]
[[play](https://go.dev/play/p/XlXHHtzCf7d)]
- **<big>And</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑and操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#And)]
[[play](https://go.dev/play/p/dTBHJMQ0zD2)]
- **<big>Or</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑or操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Or)]
[[play](https://go.dev/play/p/LitCIsDFNDA)]
- **<big>Negate</big>** : 返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Negate)]
[[play](https://go.dev/play/p/jbI8BtgFnVE)]
- **<big>Nor</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑非或nor的操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nor)]
[[play](https://go.dev/play/p/2KdCoBEOq84)]
- **<big>Nand</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑非与nand的操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nand)]
[[play](https://go.dev/play/p/Rb-FdNGpgSO)]
- **<big>Xnor</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑异或xnor的操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Xnor)]
[[play](https://go.dev/play/p/FJxko8SFbqc)]
- **<big>Watcher</big>** : Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Watcher)]
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
<h3 id="maputil"> 12. maputil 包括一些操作 map 的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
@@ -859,6 +925,72 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>HasKey</big>** : 检查 map 是否包含某个 key。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#HasKey)]
[[play](https://go.dev/play/p/isZZHOsDhFc)]
- **<big>GetOrSet</big>** : 返回给定键的值,如果不存在则设置该值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrSet)]
[[play](https://go.dev/play/p/IVQwO1OkEJC)]
- **<big>MapToStruct</big>** : 将map转成struct。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#MapToStruct)]
[[play](https://go.dev/play/p/7wYyVfX38Dp)]
- **<big>ToSortedSlicesDefault</big>** : 将map的key和value转化成两个根据key的值从小到大排序的切片value切片中元素的位置与key对应。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesDefault)]
[[play](https://go.dev/play/p/43gEM2po-qy)]
- **<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)]
@@ -883,6 +1015,12 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ConcurrentMap_Range</big>** : 为 map 中每个键和值顺序调用迭代器。 如果 iterator 返回 false则停止迭代。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_Range)]
[[play](https://go.dev/play/p/iqcy7P8P0Pr)]
- **<big>SortByKey</big>** : 对传入的map根据key进行排序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#SortByKey)]
[[play](todo)]
- **<big>GetOrDefault</big>** : 返回给定键的值,如果键不存在,则返回默认值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrDefault)]
[[play](todo)]
<h3 id="mathutil"> 13. mathutil 包实现了一些数学计算的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -928,6 +1066,18 @@ import "github.com/duke-git/lancet/v2/mathutil"
- **<big>TruncRound</big>** : 截短 n 位小数(不进行四舍五入)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#TruncRound)]
[[play](https://go.dev/play/p/aumarSHIGzP)]
- **<big>CeilToFloat</big>** : 向上舍入进一法保留n位小数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToFloat)]
[[play](https://go.dev/play/p/8hOeSADZPCo)]
- **<big>CeilToString</big>** : 向上舍入进一法保留n位小数返回字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToString)]
[[play](https://go.dev/play/p/wy5bYEyUKKG)]
- **<big>FloorToFloat</big>** : 向下舍入去尾法保留n位小数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToFloat)]
[[play](https://go.dev/play/p/vbCBrQHZEED)]
- **<big>FloorToString</big>** : 向下舍入去尾法保留n位小数返回字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToString)]
[[play](https://go.dev/play/p/Qk9KPd2IdDb)]
- **<big>Range</big>** : 根据指定的起始值和数量,创建一个数字切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Range)]
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
@@ -967,6 +1117,10 @@ import "github.com/duke-git/lancet/v2/mathutil"
- **<big>Abs</big>** : 求绝对值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Sum)]
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
- **<big>Div</big>** : 除法运算。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Div)]
[[play](https://go.dev/play/p/WLxDdGXXYat)]
<h3 id="netutil"> 14. netutil 网络包支持获取 ip 地址,发送 http 请求。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1097,7 +1251,7 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>UUIdV4</big>** : 生成 UUID v4 字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#UUIdV4)]
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的长度为 n 的随机 int 切片。
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的随机int切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandUniqueIntSlice)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandSymbolChar</big>** : 生成给定长度的随机符号字符串。
@@ -1109,6 +1263,24 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>RandFloats</big>** : 生成随机float64数字切片可以指定长度范围和精度.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloats)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandStringSlice</big>** : 生成随机字符串slice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandStringSlice)]
[[play](todo)]
- **<big>RandBool</big>** : 生成随机bool值(true or false)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBool)]
[[play](todo)]
- **<big>RandBoolSlice</big>** : 生成特定长度的随机bool slice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBoolSlice)]
[[play](todo)]
- **<big>RandIntSlice</big>** : 生成一个特定长度的随机int切片数值范围[min, max)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandIntSlice)]
[[play](todo)]
- **<big>RandFromGivenSlice</big>** : 从给定切片中随机生成元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFromGivenSlice)]
[[play](todo)]
- **<big>RandSliceFromGivenSlice</big>** : 从给定切片中生成长度为 num 的随机切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandSliceFromGivenSlice)]
[[play](todo)]
<h3 id="retry"> 17. retry 重试执行函数直到函数运行成功或被 context cancel。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1128,12 +1300,22 @@ 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)]
- **<big>BackoffStrategy</big>** : 定义计算退避间隔的方法的接口。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#BackoffStrategy)]
- **<big>RetryWithCustomBackoff</big>** : 设置自定义退避策略。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithCustomBackoff)]
[[play](https://go.dev/play/p/jIm_o2vb5Y4)]
- **<big>RetryWithLinearBackoff</big>** : 设置线性策略退避。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithLinearBackoff)]
[[play](https://go.dev/play/p/PDet2ZQZwcB)]
- **<big>RetryWithExponentialWithJitterBackoff</big>** : 设置指数策略退避。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
[[play](https://go.dev/play/p/xp1avQmn16X)]
<h3 id="slice"> 18. slice 包含操作切片的方法集合。&nbsp; &nbsp; &nbsp; &nbsp; <a href="#index">回到目录</a></h3>
@@ -1181,8 +1363,10 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/v2U2deugKuV)]
- **<big>DeleteAt</big>** : 删除切片中指定索引到的元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteAt)]
[[play](https://go.dev/play/p/800B1dPBYyd)]
- **<big>DeleteRange</big>** : 删除切片中指定开始索引到结束索引的元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteRange)]
[[play](https://go.dev/play/p/945HwiNrnle)]
- **<big>Drop</big>** : 从切片头部删除 n 个元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Drop)]
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
@@ -1234,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)]
@@ -1264,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)]
@@ -1279,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)]
@@ -1330,9 +1523,18 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Unique</big>** : 删除切片中的重复元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Unique)]
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
- **<big>UniqueBy</big>** : 对切片的每个元素调用 iteratee 函数,然后删除重复元素
- **<big>UniqueBy</big>** : 根据迭代函数返回的值,从输入切片中移除重复元素。此函数保持元素的顺序
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueBy)]
[[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>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)]
@@ -1356,7 +1558,20 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Random</big>** : 随机返回切片中元素以及下标, 当切片长度为0时返回下标-1。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Random)]
[[play](https://go.dev/play/p/UzpGQptWppw)]
- **<big>SetToDefaultIf</big>** : 根据给定给定的predicate判定函数来修改切片中的元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#SetToDefaultIf)]
[[play](https://go.dev/play/p/9AXGlPRC0-A)]
- **<big>Break</big>** : 根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Break)]
- **<big>RightPadding</big>** : 在切片的右部添加元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#RightPadding)]
[[play](https://go.dev/play/p/0_2rlLEMBXL)]
- **<big>LeftPadding</big>** : 在切片的左部添加元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#LeftPadding)]
[[play](https://go.dev/play/p/jlQVoelLl2k)]
- **<big>Frequency</big>** : 计算切片中每个元素出现的频率。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Frequency)]
[[play](todo)]
<h3 id="stream"> 19. stream 流,该包仅验证简单的 stream 实现,功能有限。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1385,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)]
@@ -1446,6 +1664,7 @@ import "github.com/duke-git/lancet/v2/stream"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ToSlice)]
[[play](https://go.dev/play/p/jI6_iZZuVFE)]
<h3 id="structs"> 20. structs 提供操作 struct, tag, field 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
@@ -1605,6 +1824,27 @@ import "github.com/duke-git/lancet/v2/strutil"
- **<big>RemoveWhiteSpace</big>** : 删除字符串中的空格。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RemoveWhiteSpace)]
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
- **<big>SubInBetween</big>** : 获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SubInBetween)]
[[play](https://go.dev/play/p/EDbaRvjeNsv)]
- **<big>HammingDistance</big>** : 计算两个字符串之间的汉明距离。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HammingDistance)]
[[play](https://go.dev/play/p/glNdQEA9HUi)]
- **<big>Concat</big>** : 拼接字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Concat)]
[[play](todo)]
- **<big>Ellipsis</big>** : 将字符串截断到指定长度,并在末尾添加省略号。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Ellipsis)]
[[play](todo)]
- **<big>Shuffle</big>** : 打乱给定字符串中的字符顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Shuffle)]
[[play](todo)]
- **<big>Rotate</big>** : 按指定的字符数旋转字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Rotate)]
[[play](todo)]
- **<big>TemplateReplace</big>** : 将模板字符串中的占位符替换为map中的相应值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#TemplateReplace)]
[[play](todo)]
<h3 id="system"> 22. system 包含 os, runtime, shell command 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1641,6 +1881,20 @@ import "github.com/duke-git/lancet/v2/system"
- **<big>GetOsBits</big>** : 获取当前操作系统位数(32/64)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#GetOsBits)]
[[play](https://go.dev/play/p/ml-_XH3gJbW)]
- **<big>StartProcess</big>** :创建进程。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#StartProcess)]
[[play](todo)]
- **<big>StopProcess</big>** : 停止进程。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#StopProcess)]
[[play](todo)]
- **<big>KillProcess</big>** : 杀掉进程。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#KillProcess)]
[[play](todo)]
- **<big>GetProcessInfo</big>** : 根据进程id获取进程信息。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#GetProcessInfo)]
[[play](todo)]
<h3 id="tuple"> 23. Tuple 包实现一个元组数据类型。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1876,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)]
@@ -1944,7 +2198,7 @@ import "github.com/duke-git/lancet/v2/xerror"
## 如何贡献代码
#### [贡献代码指南](./CONTRIBUTING.zh-CN.md)
#### [代码贡献指南](./CONTRIBUTION.zh-CN.md)
## 贡献者
@@ -1953,3 +2207,7 @@ import "github.com/duke-git/lancet/v2/xerror"
<a href="https://github.com/duke-git/lancet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
</a>
## GitHub Stars
[![Star History Chart](https://api.star-history.com/svg?repos=duke-git/lancet&type=Date)](https://star-history.com/#duke-git/lancet&Date)

View File

@@ -1,6 +1,7 @@
package compare
import (
"encoding/json"
"testing"
"time"
@@ -11,6 +12,7 @@ func TestEqual(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEqual")
// basic type
assert.Equal(true, Equal(1, 1))
assert.Equal(true, Equal(int64(1), int64(1)))
assert.Equal(true, Equal("a", "a"))
@@ -25,6 +27,7 @@ func TestEqual(t *testing.T) {
assert.Equal(false, Equal([]int{1, 2}, []int{1, 2, 3}))
assert.Equal(false, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a"}))
// time
time1 := time.Now()
time2 := time1.Add(time.Second)
time3 := time1.Add(time.Second)
@@ -32,6 +35,7 @@ func TestEqual(t *testing.T) {
assert.Equal(false, Equal(time1, time2))
assert.Equal(true, Equal(time2, time3))
// struct
st1 := struct {
A string
B string
@@ -58,6 +62,19 @@ func TestEqual(t *testing.T) {
assert.Equal(true, Equal(st1, st2))
assert.Equal(false, Equal(st1, st3))
//byte slice
bs1 := []byte("hello")
bs2 := []byte("hello")
assert.Equal(true, Equal(bs1, bs2))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`123`), &jsonNumber2)
assert.Equal(true, Equal(jsonNumber1, jsonNumber2))
}
func TestEqualValue(t *testing.T) {
@@ -69,6 +86,13 @@ func TestEqualValue(t *testing.T) {
assert.Equal(true, EqualValue(1, "1"))
assert.Equal(false, EqualValue(1, "2"))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`123`), &jsonNumber2)
assert.Equal(true, EqualValue(jsonNumber1, 123))
}
func TestLessThan(t *testing.T) {
@@ -85,6 +109,17 @@ func TestLessThan(t *testing.T) {
assert.Equal(false, LessThan(1, 1))
assert.Equal(false, LessThan(1, int64(1)))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, LessThan(bs1, bs2))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, LessThan(jsonNumber1, jsonNumber2))
}
func TestGreaterThan(t *testing.T) {
@@ -102,6 +137,17 @@ func TestGreaterThan(t *testing.T) {
assert.Equal(false, GreaterThan(1, 2))
assert.Equal(false, GreaterThan(int64(2), 1))
assert.Equal(false, GreaterThan("b", "c"))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, GreaterThan(bs2, bs1))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, GreaterThan(jsonNumber2, jsonNumber1))
}
func TestLessOrEqual(t *testing.T) {
@@ -119,6 +165,17 @@ func TestLessOrEqual(t *testing.T) {
assert.Equal(false, LessOrEqual(2, 1))
assert.Equal(false, LessOrEqual(1, int64(2)))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, LessOrEqual(bs1, bs2))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, LessOrEqual(jsonNumber1, jsonNumber2))
}
func TestGreaterOrEqual(t *testing.T) {
@@ -137,6 +194,17 @@ func TestGreaterOrEqual(t *testing.T) {
assert.Equal(false, GreaterOrEqual(1, 2))
assert.Equal(false, GreaterOrEqual(int64(2), 1))
assert.Equal(false, GreaterOrEqual("b", "c"))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, GreaterOrEqual(bs2, bs1))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, GreaterOrEqual(jsonNumber2, jsonNumber1))
}
func TestInDelta(t *testing.T) {

View File

@@ -49,9 +49,7 @@ func Or[T, U any](a T, b U) bool {
// Xor returns true if a or b but not both is truthy.
// Play: https://go.dev/play/p/gObZrW7ZbG8
func Xor[T, U any](a T, b U) bool {
valA := Bool(a)
valB := Bool(b)
return (valA || valB) && valA != valB
return Bool(a) != Bool(b)
}
// Nor returns true if neither a nor b is truthy.
@@ -63,9 +61,7 @@ func Nor[T, U any](a T, b U) bool {
// Xnor returns true if both a and b or neither a nor b are truthy.
// Play: https://go.dev/play/p/OuDB9g51643
func Xnor[T, U any](a T, b U) bool {
valA := Bool(a)
valB := Bool(b)
return (valA && valB) || (!valA && !valB)
return Bool(a) == Bool(b)
}
// Nand returns false if both a and b are truthy.

View File

@@ -6,6 +6,7 @@ package convertor
import (
"bytes"
"encoding/base64"
"encoding/binary"
"encoding/gob"
"encoding/json"
@@ -394,3 +395,91 @@ func GbkToUtf8(bs []byte) ([]byte, error) {
b, err := io.ReadAll(r)
return b, err
}
// ToStdBase64 convert data to standard base64 encoding.
// Play: https://go.dev/play/p/_fLJqJD3NMo
func ToStdBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
case []byte:
return base64.StdEncoding.EncodeToString(value.([]byte))
case string:
return base64.StdEncoding.EncodeToString([]byte(value.(string)))
case error:
return base64.StdEncoding.EncodeToString([]byte(value.(error).Error()))
default:
marshal, err := json.Marshal(value)
if err != nil {
return ""
}
return base64.StdEncoding.EncodeToString(marshal)
}
}
// ToUrlBase64 convert data to URL base64 encoding.
// Play: https://go.dev/play/p/C_d0GlvEeUR
func ToUrlBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
case []byte:
return base64.URLEncoding.EncodeToString(value.([]byte))
case string:
return base64.URLEncoding.EncodeToString([]byte(value.(string)))
case error:
return base64.URLEncoding.EncodeToString([]byte(value.(error).Error()))
default:
marshal, err := json.Marshal(value)
if err != nil {
return ""
}
return base64.URLEncoding.EncodeToString(marshal)
}
}
// ToRawStdBase64 convert data to raw standard base64 encoding.
// Play: https://go.dev/play/p/wSAr3sfkDcv
func ToRawStdBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
case []byte:
return base64.RawStdEncoding.EncodeToString(value.([]byte))
case string:
return base64.RawStdEncoding.EncodeToString([]byte(value.(string)))
case error:
return base64.RawStdEncoding.EncodeToString([]byte(value.(error).Error()))
default:
marshal, err := json.Marshal(value)
if err != nil {
return ""
}
return base64.RawStdEncoding.EncodeToString(marshal)
}
}
// ToRawUrlBase64 convert data to raw URL base64 encoding.
// Play: https://go.dev/play/p/HwdDPFcza1O
func ToRawUrlBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
case []byte:
return base64.RawURLEncoding.EncodeToString(value.([]byte))
case string:
return base64.RawURLEncoding.EncodeToString([]byte(value.(string)))
case error:
return base64.RawURLEncoding.EncodeToString([]byte(value.(error).Error()))
default:
marshal, err := json.Marshal(value)
if err != nil {
return ""
}
return base64.RawURLEncoding.EncodeToString(marshal)
}
}

View File

@@ -1,6 +1,7 @@
package convertor
import (
"errors"
"fmt"
"reflect"
"strconv"
@@ -391,3 +392,182 @@ func ExampleGbkToUtf8() {
// true
// hello
}
func ExampleToStdBase64() {
// if you want to see the result, please use 'base64.StdEncoding.DecodeString()' to decode the result
afterEncode := ToStdBase64(nil)
fmt.Println(afterEncode)
stringVal := "hello"
afterEncode = ToStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = ToStdBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = ToStdBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = ToStdBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = ToStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = ToStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = ToStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
//
// aGVsbG8=
// aGVsbG8=
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
// MTIzLjQ1Ng==
// dHJ1ZQ==
// ZXJy
}
func ExampleToUrlBase64() {
// if you want to see the result, please use 'base64.URLEncoding.DecodeString()' to decode the result
stringVal := "hello"
afterEncode := ToUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = ToUrlBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = ToUrlBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = ToUrlBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = ToUrlBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = ToUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = ToUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8=
// aGVsbG8=
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
// MTIzLjQ1Ng==
// dHJ1ZQ==
// ZXJy
}
func ExampleToRawStdBase64() {
// if you want to see the result, please use 'base64.RawStdEncoding.DecodeString()' to decode the result
stringVal := "hello"
afterEncode := ToRawStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = ToRawStdBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = ToRawStdBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = ToRawStdBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = ToRawStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = ToRawStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = ToRawStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
func ExampleToRawUrlBase64() {
// if you want to see the result, please use 'base64.RawURLEncoding.DecodeString()' to decode the result
stringVal := "hello"
afterEncode := ToRawUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = ToRawUrlBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = ToRawUrlBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = ToRawUrlBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = ToRawUrlBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = ToRawUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = ToRawUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}

View File

@@ -1,11 +1,15 @@
package convertor
import (
"encoding/base64"
"errors"
"fmt"
"io"
"reflect"
"strconv"
"testing"
"unicode/utf8"
"unsafe"
"github.com/duke-git/lancet/v2/internal"
"github.com/duke-git/lancet/v2/slice"
@@ -461,3 +465,279 @@ func TestGbkToUtf8(t *testing.T) {
assert.Equal(true, utf8.Valid(utf8Data))
assert.Equal("hello", string(utf8Data))
}
func TestToStdBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToStdBase64")
r1 := ToStdBase64("abc")
d1, _ := base64.StdEncoding.DecodeString(r1)
assert.Equal("abc", string(d1))
r2 := ToStdBase64([]byte("abc"))
d2, _ := base64.StdEncoding.DecodeString(r2)
assert.Equal("abc", string(d2))
r3 := ToStdBase64(123)
d3, _ := base64.StdEncoding.DecodeString(r3)
assert.Equal("123", string(d3))
r4 := ToStdBase64(11.11)
d4, _ := base64.StdEncoding.DecodeString(r4)
assert.Equal("11.11", string(d4))
r5 := ToStdBase64(map[string]any{"name": "duke", "quantity": 1})
d5, _ := base64.StdEncoding.DecodeString(r5)
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
r6 := ToStdBase64([]int64{7, 5, 9, 4, 23})
d6, _ := base64.StdEncoding.DecodeString(r6)
assert.Equal("[7,5,9,4,23]", string(d6))
r7 := ToStdBase64([]string{"7", "5", "9", "4", "23"})
d7, _ := base64.StdEncoding.DecodeString(r7)
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
r8 := ToStdBase64(nil)
d8, _ := base64.StdEncoding.DecodeString(r8)
assert.Equal("", string(d8))
ch := make(chan int, 3)
ch <- 1
ch <- 2
r9 := ToStdBase64(ch)
d9, _ := base64.StdEncoding.DecodeString(r9)
assert.Equal("", string(d9))
r10 := ToStdBase64(io.EOF)
d10, _ := base64.StdEncoding.DecodeString(r10)
assert.Equal("EOF", string(d10))
r11 := ToStdBase64(errors.New("test"))
d11, _ := base64.StdEncoding.DecodeString(r11)
assert.Equal("test", string(d11))
typedNil := (*int)(nil)
r12 := ToStdBase64(typedNil)
d12, _ := base64.StdEncoding.DecodeString(r12)
assert.Equal("", string(d12))
type nilInterface interface {
}
var nI nilInterface = nil
d13, _ := base64.StdEncoding.DecodeString(ToStdBase64(nI))
assert.Equal("", string(d13))
var p unsafe.Pointer
d14, _ := base64.StdEncoding.DecodeString(ToStdBase64(p))
assert.Equal("", string(d14))
}
func TestToUrlBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToUrlBase64")
r1 := ToUrlBase64("abc")
d1, _ := base64.URLEncoding.DecodeString(r1)
assert.Equal("abc", string(d1))
r2 := ToUrlBase64([]byte("abc"))
d2, _ := base64.URLEncoding.DecodeString(r2)
assert.Equal("abc", string(d2))
r3 := ToUrlBase64(123)
d3, _ := base64.URLEncoding.DecodeString(r3)
assert.Equal("123", string(d3))
r4 := ToUrlBase64(11.11)
d4, _ := base64.URLEncoding.DecodeString(r4)
assert.Equal("11.11", string(d4))
r5 := ToUrlBase64(map[string]any{"name": "duke", "quantity": 1})
d5, _ := base64.URLEncoding.DecodeString(r5)
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
r6 := ToUrlBase64([]int64{7, 5, 9, 4, 23})
d6, _ := base64.URLEncoding.DecodeString(r6)
assert.Equal("[7,5,9,4,23]", string(d6))
r7 := ToUrlBase64([]string{"7", "5", "9", "4", "23"})
d7, _ := base64.URLEncoding.DecodeString(r7)
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
r8 := ToUrlBase64(nil)
d8, _ := base64.URLEncoding.DecodeString(r8)
assert.Equal("", string(d8))
ch := make(chan int, 3)
ch <- 1
ch <- 2
r9 := ToUrlBase64(ch)
d9, _ := base64.URLEncoding.DecodeString(r9)
assert.Equal("", string(d9))
r10 := ToUrlBase64(io.EOF)
d10, _ := base64.URLEncoding.DecodeString(r10)
assert.Equal("EOF", string(d10))
r11 := ToUrlBase64(errors.New("test"))
d11, _ := base64.URLEncoding.DecodeString(r11)
assert.Equal("test", string(d11))
typedNil := (*int)(nil)
r12 := ToUrlBase64(typedNil)
d12, _ := base64.URLEncoding.DecodeString(r12)
assert.Equal("", string(d12))
type nilInterface interface {
}
var nI nilInterface = nil
d13, _ := base64.URLEncoding.DecodeString(ToUrlBase64(nI))
assert.Equal("", string(d13))
var p unsafe.Pointer
d14, _ := base64.URLEncoding.DecodeString(ToUrlBase64(p))
assert.Equal("", string(d14))
r15 := ToUrlBase64("4+3/4?=")
d15, _ := base64.URLEncoding.DecodeString(r15)
assert.Equal("4+3/4?=", string(d15))
}
func TestToRawStdBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToRawStdBase64")
r1 := ToRawStdBase64("abc")
d1, _ := base64.RawStdEncoding.DecodeString(r1)
assert.Equal("abc", string(d1))
r2 := ToRawStdBase64([]byte("abc"))
d2, _ := base64.RawStdEncoding.DecodeString(r2)
assert.Equal("abc", string(d2))
r3 := ToRawStdBase64(123)
d3, _ := base64.RawStdEncoding.DecodeString(r3)
assert.Equal("123", string(d3))
r4 := ToRawStdBase64(11.11)
d4, _ := base64.RawStdEncoding.DecodeString(r4)
assert.Equal("11.11", string(d4))
r5 := ToRawStdBase64(map[string]any{"name": "duke", "quantity": 1})
d5, _ := base64.RawStdEncoding.DecodeString(r5)
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
r6 := ToRawStdBase64([]int64{7, 5, 9, 4, 23})
d6, _ := base64.RawStdEncoding.DecodeString(r6)
assert.Equal("[7,5,9,4,23]", string(d6))
r7 := ToRawStdBase64([]string{"7", "5", "9", "4", "23"})
d7, _ := base64.RawStdEncoding.DecodeString(r7)
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
r8 := ToRawStdBase64(nil)
d8, _ := base64.RawStdEncoding.DecodeString(r8)
assert.Equal("", string(d8))
ch := make(chan int, 3)
ch <- 1
ch <- 2
r9 := ToRawStdBase64(ch)
d9, _ := base64.RawStdEncoding.DecodeString(r9)
assert.Equal("", string(d9))
r10 := ToRawStdBase64(io.EOF)
d10, _ := base64.RawStdEncoding.DecodeString(r10)
assert.Equal("EOF", string(d10))
r11 := ToRawStdBase64(errors.New("test"))
d11, _ := base64.RawStdEncoding.DecodeString(r11)
assert.Equal("test", string(d11))
typedNil := (*int)(nil)
r12 := ToRawStdBase64(typedNil)
d12, _ := base64.RawStdEncoding.DecodeString(r12)
assert.Equal("", string(d12))
type nilInterface interface {
}
var nI nilInterface = nil
d13, _ := base64.RawStdEncoding.DecodeString(ToRawStdBase64(nI))
assert.Equal("", string(d13))
var p unsafe.Pointer
d14, _ := base64.RawStdEncoding.DecodeString(ToRawStdBase64(p))
assert.Equal("", string(d14))
}
func TestToRawUrlBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToRawUrlBase64")
r1 := ToRawUrlBase64("abc")
d1, _ := base64.RawURLEncoding.DecodeString(r1)
assert.Equal("abc", string(d1))
r2 := ToRawUrlBase64([]byte("abc"))
d2, _ := base64.RawURLEncoding.DecodeString(r2)
assert.Equal("abc", string(d2))
r3 := ToRawUrlBase64(123)
d3, _ := base64.RawURLEncoding.DecodeString(r3)
assert.Equal("123", string(d3))
r4 := ToRawUrlBase64(11.11)
d4, _ := base64.RawURLEncoding.DecodeString(r4)
assert.Equal("11.11", string(d4))
r5 := ToRawUrlBase64(map[string]any{"name": "duke", "quantity": 1})
d5, _ := base64.RawURLEncoding.DecodeString(r5)
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
r6 := ToRawUrlBase64([]int64{7, 5, 9, 4, 23})
d6, _ := base64.RawURLEncoding.DecodeString(r6)
assert.Equal("[7,5,9,4,23]", string(d6))
r7 := ToRawUrlBase64([]string{"7", "5", "9", "4", "23"})
d7, _ := base64.RawURLEncoding.DecodeString(r7)
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
r8 := ToRawUrlBase64(nil)
d8, _ := base64.RawURLEncoding.DecodeString(r8)
assert.Equal("", string(d8))
ch := make(chan int, 3)
ch <- 1
ch <- 2
r9 := ToRawUrlBase64(ch)
d9, _ := base64.RawURLEncoding.DecodeString(r9)
assert.Equal("", string(d9))
r10 := ToRawUrlBase64(io.EOF)
d10, _ := base64.RawURLEncoding.DecodeString(r10)
assert.Equal("EOF", string(d10))
r11 := ToRawUrlBase64(errors.New("test"))
d11, _ := base64.RawURLEncoding.DecodeString(r11)
assert.Equal("test", string(d11))
typedNil := (*int)(nil)
r12 := ToRawUrlBase64(typedNil)
d12, _ := base64.RawURLEncoding.DecodeString(r12)
assert.Equal("", string(d12))
type nilInterface interface {
}
var nI nilInterface = nil
d13, _ := base64.RawURLEncoding.DecodeString(ToRawUrlBase64(nI))
assert.Equal("", string(d13))
var p unsafe.Pointer
d14, _ := base64.RawURLEncoding.DecodeString(ToRawUrlBase64(p))
assert.Equal("", string(d14))
r15 := ToRawUrlBase64("4+3/4?=")
d15, _ := base64.RawURLEncoding.DecodeString(r15)
assert.Equal("4+3/4?=", string(d15))
}

View File

@@ -76,6 +76,11 @@ func AesEcbDecrypt(encrypted, key []byte) []byte {
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/IOq_g8_lKZD
func AesCbcEncrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
}
block, _ := aes.NewCipher(key)
data = pkcs7Padding(data, block.BlockSize())
@@ -95,6 +100,11 @@ func AesCbcEncrypt(data, key []byte) []byte {
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/IOq_g8_lKZD
func AesCbcDecrypt(encrypted, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
}
block, _ := aes.NewCipher(key)
iv := encrypted[:aes.BlockSize]
@@ -111,6 +121,11 @@ func AesCbcDecrypt(encrypted, key []byte) []byte {
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/SpaZO0-5Nsp
func AesCtrCrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
}
block, _ := aes.NewCipher(key)
iv := bytes.Repeat([]byte("1"), block.BlockSize())
@@ -126,6 +141,11 @@ func AesCtrCrypt(data, key []byte) []byte {
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/tfkF10B13kH
func AesCfbEncrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
@@ -148,6 +168,11 @@ func AesCfbEncrypt(data, key []byte) []byte {
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/tfkF10B13kH
func AesCfbDecrypt(encrypted, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
}
if len(encrypted) < aes.BlockSize {
panic("encrypted data is too short")
}
@@ -167,6 +192,11 @@ func AesCfbDecrypt(encrypted, key []byte) []byte {
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/VtHxtkUj-3F
func AesOfbEncrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
@@ -189,6 +219,11 @@ func AesOfbEncrypt(data, key []byte) []byte {
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/VtHxtkUj-3F
func AesOfbDecrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
@@ -209,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
@@ -255,6 +340,11 @@ func DesEcbDecrypt(encrypted, key []byte) []byte {
// len(key) should be 8.
// Play: https://go.dev/play/p/4cC4QvWfe3_1
func DesCbcEncrypt(data, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
}
block, _ := des.NewCipher(key)
data = pkcs7Padding(data, block.BlockSize())
@@ -275,6 +365,11 @@ func DesCbcEncrypt(data, key []byte) []byte {
// len(key) should be 8.
// Play: https://go.dev/play/p/4cC4QvWfe3_1
func DesCbcDecrypt(encrypted, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
}
block, _ := des.NewCipher(key)
iv := encrypted[:des.BlockSize]
@@ -291,6 +386,11 @@ func DesCbcDecrypt(encrypted, key []byte) []byte {
// len(key) should be 8.
// Play: https://go.dev/play/p/9-T6OjKpcdw
func DesCtrCrypt(data, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
}
block, _ := des.NewCipher(key)
iv := bytes.Repeat([]byte("1"), block.BlockSize())
@@ -306,6 +406,11 @@ func DesCtrCrypt(data, key []byte) []byte {
// len(key) should be 8.
// Play: https://go.dev/play/p/y-eNxcFBlxL
func DesCfbEncrypt(data, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
}
block, err := des.NewCipher(key)
if err != nil {
panic(err)
@@ -327,6 +432,11 @@ func DesCfbEncrypt(data, key []byte) []byte {
// len(encrypted) should be great than 16, len(key) should be 8.
// Play: https://go.dev/play/p/y-eNxcFBlxL
func DesCfbDecrypt(encrypted, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
}
block, _ := des.NewCipher(key)
if len(encrypted) < des.BlockSize {
panic("encrypted data is too short")
@@ -341,9 +451,14 @@ func DesCfbDecrypt(encrypted, key []byte) []byte {
}
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
// len(key) should be 16, 24 or 32.
// len(key) should be 8.
// Play: https://go.dev/play/p/74KmNadjN1J
func DesOfbEncrypt(data, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
}
block, err := des.NewCipher(key)
if err != nil {
panic(err)
@@ -365,6 +480,11 @@ func DesOfbEncrypt(data, key []byte) []byte {
// len(key) should be 8.
// Play: https://go.dev/play/p/74KmNadjN1J
func DesOfbDecrypt(data, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
}
block, err := des.NewCipher(key)
if err != nil {
panic(err)

View File

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

View File

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

View File

@@ -123,6 +123,25 @@ func (hm *HashMap) Iterate(iteratee func(key, value any)) {
}
}
// FilterByValue returns a filtered HashMap.
// If any value is not matching the perdicate function then it returns nil
// otherwise it returns the HashMap with selected values.
func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap {
var filteredHM *HashMap
if hm.size > 0 {
for i := 0; i < len(hm.table); i++ {
item := hm.table[i]
if item != nil && perdicate(item.value) {
if filteredHM == nil {
filteredHM = NewHashMap()
}
filteredHM.Put(item.key, item.value)
}
}
}
return filteredHM
}
// Keys returns a slice of the hashmap's keys (random order)
func (hm *HashMap) Keys() []any {
keys := make([]any, int(hm.size))
@@ -168,6 +187,11 @@ func (hm *HashMap) resize() {
}
}
// Size returns current size of Hashmap
func (hm *HashMap) Size() uint64 {
return hm.size
}
func (hm *HashMap) hash(key any) uint64 {
h := fnv.New64a()
_, _ = h.Write([]byte(fmt.Sprintf("%v", key)))

View File

@@ -105,3 +105,24 @@ func TestHashMap_GetOrDefault(t *testing.T) {
assert.Equal(1, hm.GetOrDefault("a", 5))
assert.Equal(5, hm.GetOrDefault("d", 5))
}
func TestHashMap_FilterByValue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHashMap_FilterByValue")
hm := NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
hm.Put("d", 4)
hm.Put("e", 5)
hm.Put("f", 6)
filteredHM := hm.FilterByValue(func(value any) bool {
return value.(int) == 1 || value.(int) == 3
})
assert.Equal(uint64(2), filteredHM.Size())
}

View File

@@ -90,6 +90,35 @@ func lastIndexOf[T any](o T, e []T, start int, end int) int {
return -1
}
// LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying the
// functional predicate f(T) bool
// if not found return -1.
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int {
index := -1
data := l.getList()
for i := len(data) - 1; i >= 0; i-- {
if f(data[i]) {
index = i
break
}
}
return index
}
// IndexOfFunc returns the first index satisfying the functional predicate f(v) bool
// if not found return -1.
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int {
index := -1
data := l.getList()
for i, v := range data {
if f(v) {
index = i
break
}
}
return index
}
// get returns the element at the specified position in this list.
func get[T any](o []T, index int) *T {
return &o[index]

View File

@@ -1,8 +1,9 @@
package datastructure
import (
"github.com/duke-git/lancet/v2/internal"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestCopyOnWriteList_ValueOf(t *testing.T) {
@@ -233,3 +234,35 @@ func TestCopyOnWriteList_SubList(t *testing.T) {
subList = list.SubList(11, 1)
assert.Equal([]int{}, subList)
}
func TestCopyOnWriteListIndexOfFunc(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIndexOfFunc")
list := NewCopyOnWriteList([]int{1, 2, 3})
i := list.IndexOfFunc(func(a int) bool { return a == 1 })
assert.Equal(0, i)
i = list.IndexOfFunc(func(a int) bool { return a == 4 })
assert.Equal(-1, i)
}
func TestNewCopyOnWriteListLastIndexOfFunc(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLastIndexOfFunc")
list := NewCopyOnWriteList([]int{1, 2, 3, 3, 3, 3, 4, 5, 6, 9})
i := list.LastIndexOfFunc(func(a int) bool { return a == 3 })
assert.Equal(5, i)
i = list.LastIndexOfFunc(func(a int) bool { return a == 10 })
assert.Equal(-1, i)
i = list.LastIndexOfFunc(func(a int) bool { return a == 4 })
assert.Equal(6, i)
i = list.LastIndexOfFunc(func(a int) bool { return a == 1 })
assert.Equal(0, i)
}

View File

@@ -0,0 +1,108 @@
package optional
import (
"sync"
)
// Optional is a type that may or may not contain a non-nil value.
type Optional[T any] struct {
value *T
mu *sync.RWMutex
}
// Default returns an default Optional instance.
func Default[T any]() Optional[T] {
return Optional[T]{mu: &sync.RWMutex{}}
}
// Of returns an Optional with a non-nil value.
func Of[T any](value T) Optional[T] {
return Optional[T]{value: &value, mu: &sync.RWMutex{}}
}
// FromNillable returns an Optional for a given value, which may be nil.
func FromNillable[T any](value *T) Optional[T] {
if value == nil {
return Default[T]()
}
return Optional[T]{value: value, mu: &sync.RWMutex{}}
}
// IsNotNil checks if there is a value present.
func (o Optional[T]) IsNotNil() bool {
o.mu.RLock()
defer o.mu.RUnlock()
return o.value != nil
}
// IsNil checks if the Optional is nil.
func (o Optional[T]) IsNil() bool {
return !o.IsNotNil()
}
// IfNotNil performs the given action with the value if a value is not nil.
func (o Optional[T]) IfNotNil(action func(value T)) {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value != nil {
action(*o.value)
}
}
// IfNotNilOrElse performs the action with the value if present, otherwise performs the fallback action.
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func()) {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value != nil {
action(*o.value)
} else {
fallbackAction()
}
}
// Unwarp returns the value if not nil, otherwise panics.
func (o Optional[T]) Unwarp() T {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value == nil {
panic("Optional.Get: no value present")
}
return *o.value
}
// OrElse returns the value if is not nil, otherwise returns other.
func (o Optional[T]) OrElse(other T) T {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value != nil {
return *o.value
}
return other
}
// OrElseGet returns the value if is not nil, otherwise invokes action and returns the result.
func (o Optional[T]) OrElseGet(action func() T) T {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value != nil {
return *o.value
}
return action()
}
// OrElseTrigger returns the value if present, otherwise returns an error.
func (o Optional[T]) OrElseTrigger(errorHandler func() error) (T, error) {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value == nil {
return *new(T), errorHandler()
}
return *o.value, nil
}

View File

@@ -0,0 +1,151 @@
package optional
import (
"errors"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestDefault(t *testing.T) {
assert := internal.NewAssert(t, "TestEmpty")
opt := Default[int]()
assert.ShouldBeTrue(opt.IsNil())
}
func TestOf(t *testing.T) {
assert := internal.NewAssert(t, "TestOf")
value := 42
opt := Of(value)
assert.ShouldBeTrue(opt.IsNotNil())
assert.Equal(opt.Unwarp(), value)
}
func TestFromNillable(t *testing.T) {
assert := internal.NewAssert(t, "TestOfNullable")
var value *int = nil
opt := FromNillable(value)
assert.ShouldBeFalse(opt.IsNotNil())
value = new(int)
*value = 42
opt = FromNillable(value)
assert.ShouldBeTrue(opt.IsNotNil())
}
func TestOrElse(t *testing.T) {
assert := internal.NewAssert(t, "TestOrElse")
optDefault := Default[int]()
defaultValue := 100
val := optDefault.OrElse(defaultValue)
assert.Equal(val, defaultValue)
optWithValue := Of(42)
val = optWithValue.OrElse(defaultValue)
assert.Equal(val, 42)
}
func TestOrElseGetHappyPath(t *testing.T) {
assert := internal.NewAssert(t, "TestOrElseGetHappyPath")
optWithValue := Of(42)
action := func() int { return 100 }
val := optWithValue.OrElseGet(action)
assert.Equal(val, 42)
}
func TestOrElseGet(t *testing.T) {
assert := internal.NewAssert(t, "TestOrElseGet")
optDefault := Default[int]()
action := func() int { return 100 }
val := optDefault.OrElseGet(action)
assert.Equal(val, action())
}
func TestOrElseTrigger(t *testing.T) {
assert := internal.NewAssert(t, "OrElseTrigger")
optDefault := Default[int]()
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
assert.Equal(err.Error(), "no value")
optWithValue := Of(42)
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
assert.IsNil(err)
assert.Equal(val, 42)
}
func TestIfNotNil(t *testing.T) {
assert := internal.NewAssert(t, "IfNotNil")
called := false
action := func(value int) { called = true }
optDefault := Default[int]()
optDefault.IfNotNil(action)
assert.ShouldBeFalse(called)
called = false // Reset for next test
optWithValue := Of(42)
optWithValue.IfNotNil(action)
assert.ShouldBeTrue(called)
}
func TestIfNotNilOrElse(t *testing.T) {
assert := internal.NewAssert(t, "TestIfNotNilOrElse")
// Test when value is present
calledWithValue := false
valueAction := func(value int) { calledWithValue = true }
fallbackAction := func() { t.Errorf("Empty action should not be called when value is present") }
optWithValue := Of(42)
optWithValue.IfNotNilOrElse(valueAction, fallbackAction)
assert.ShouldBeTrue(calledWithValue)
// Test when value is not present
calledWithEmpty := false
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
fallbackAction = func() { calledWithEmpty = true }
optDefault := Default[int]()
optDefault.IfNotNilOrElse(valueAction, fallbackAction)
assert.ShouldBeTrue(calledWithEmpty)
}
func TestGetWithPanicStandard(t *testing.T) {
assert := internal.NewAssert(t, "TestGetWithPanicStandard")
// Test when value is present
optWithValue := Of(42)
func() {
defer func() {
r := recover()
assert.IsNil(r)
}()
val := optWithValue.Unwarp()
if val != 42 {
t.Errorf("Expected Unwarp to return 42, got %v", val)
}
}()
// Test when value is not present
optDefault := Default[int]()
func() {
defer func() {
r := recover()
assert.IsNotNil(r)
}()
_ = optDefault.Unwarp()
}()
}

View File

@@ -4,19 +4,21 @@
// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate.
package datastructure
import "sort"
// Set is a data container, like slice, but element of set is not duplicate.
type Set[T comparable] map[T]struct{}
// NewSet return a instance of set
func NewSet[T comparable](items ...T) Set[T] {
set := make(Set[T])
// New create a instance of set from given values.
func New[T comparable](items ...T) Set[T] {
set := make(Set[T], len(items))
set.Add(items...)
return set
}
// NewSetFromSlice create a set from slice
func NewSetFromSlice[T comparable](items []T) Set[T] {
set := make(Set[T])
// FromSlice create a set from given slice.
func FromSlice[T comparable](items []T) Set[T] {
set := make(Set[T], len(items))
for _, item := range items {
set.Add(item)
}
@@ -77,8 +79,7 @@ func (s Set[T]) ContainAll(other Set[T]) bool {
// Clone return a copy of set
func (s Set[T]) Clone() Set[T] {
set := NewSet[T]()
set.Add(s.Values()...)
set := FromSlice(s.ToSlice())
return set
}
@@ -116,14 +117,11 @@ func (s Set[T]) Size() int {
}
// Values return all values of set
// Deprecated: Values function is deprecated and will be removed in future versions. Please use ToSlice() function instead.
//
// The ToSlice() function provides the same functionality as Values and returns a slice containing all values of the set.
func (s Set[T]) Values() []T {
result := make([]T, 0, len(s))
s.Iterate(func(value T) {
result = append(result, value)
})
return result
return s.ToSlice()
}
// Union creates a new set contain all element of set s and other
@@ -135,7 +133,7 @@ func (s Set[T]) Union(other Set[T]) Set[T] {
// Intersection creates a new set whose element both be contained in set s and other
func (s Set[T]) Intersection(other Set[T]) Set[T] {
set := NewSet[T]()
set := New[T]()
s.Iterate(func(value T) {
if other.Contain(value) {
set.Add(value)
@@ -147,7 +145,7 @@ func (s Set[T]) Intersection(other Set[T]) Set[T] {
// SymmetricDifference creates a new set whose element is in set1 or set2, but not in both sets
func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
set := NewSet[T]()
set := New[T]()
s.Iterate(func(value T) {
if !other.Contain(value) {
set.Add(value)
@@ -163,9 +161,9 @@ func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
return set
}
// Minus creates an set of whose element in origin set but not in compared set
// Minus creates a set of whose element in origin set but not in compared set
func (s Set[T]) Minus(comparedSet Set[T]) Set[T] {
set := NewSet[T]()
set := New[T]()
s.Iterate(func(value T) {
if !comparedSet.Contain(value) {
@@ -189,11 +187,34 @@ func (s Set[T]) EachWithBreak(iteratee func(item T) bool) {
// Pop delete the top element of set then return it, if set is empty, return nil-value of T and false.
func (s Set[T]) Pop() (v T, ok bool) {
if len(s) > 0 {
items := s.Values()
item := items[len(s)-1]
delete(s, item)
return item, true
for item := range s {
v = item
delete(s, item)
return v, true
}
}
return v, false
}
// ToSlice returns a slice containing all values of the set.
func (s Set[T]) ToSlice() []T {
if s.IsEmpty() {
return []T{}
}
result := make([]T, 0, s.Size())
s.Iterate(func(value T) {
result = append(result, value)
})
return result
}
// ToSortedSlice returns a sorted slice containing all values of the set.
func (s Set[T]) ToSortedSlice(less func(v1, v2 T) bool) []T {
result := s.ToSlice()
sort.Slice(result, func(i, j int) bool {
return less(result[i], result[j])
})
return result
}

View File

@@ -1,23 +1,25 @@
package datastructure
import (
"reflect"
"sort"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestSet_NewSetFromSlice(t *testing.T) {
func TestSet_FromSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_NewSetFromSlice")
assert := internal.NewAssert(t, "TestSet_FromSlice")
s1 := NewSetFromSlice([]int{1, 2, 2, 3})
s1 := FromSlice([]int{1, 2, 2, 3})
assert.Equal(3, s1.Size())
assert.Equal(true, s1.Contain(1))
assert.Equal(true, s1.Contain(2))
assert.Equal(true, s1.Contain(3))
s2 := NewSetFromSlice([]int{})
s2 := FromSlice([]int{})
assert.Equal(0, s2.Size())
}
@@ -26,10 +28,10 @@ func TestSet_Add(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Add")
set := NewSet[int]()
set := New[int]()
set.Add(1, 2, 3)
cmpSet := NewSet(1, 2, 3)
cmpSet := New(1, 2, 3)
assert.Equal(true, set.Equal(cmpSet))
}
@@ -39,12 +41,12 @@ func TestSet_AddIfNotExist(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_AddIfNotExist")
set := NewSet[int]()
set := New[int]()
set.Add(1, 2, 3)
assert.Equal(false, set.AddIfNotExist(1))
assert.Equal(true, set.AddIfNotExist(4))
assert.Equal(NewSet(1, 2, 3, 4), set)
assert.Equal(New(1, 2, 3, 4), set)
}
func TestSet_AddIfNotExistBy(t *testing.T) {
@@ -52,7 +54,7 @@ func TestSet_AddIfNotExistBy(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_AddIfNotExistBy")
set := NewSet[int]()
set := New[int]()
set.Add(1, 2)
ok := set.AddIfNotExistBy(3, func(val int) bool {
@@ -75,7 +77,7 @@ func TestSet_Contain(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Contain")
set := NewSet[int]()
set := New[int]()
set.Add(1, 2, 3)
assert.Equal(true, set.Contain(1))
@@ -87,9 +89,9 @@ func TestSet_ContainAll(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_ContainAll")
set1 := NewSet(1, 2, 3)
set2 := NewSet(1, 2)
set3 := NewSet(1, 2, 3, 4)
set1 := New(1, 2, 3)
set2 := New(1, 2)
set3 := New(1, 2, 3, 4)
assert.Equal(true, set1.ContainAll(set2))
assert.Equal(false, set1.ContainAll(set3))
@@ -100,7 +102,7 @@ func TestSet_Clone(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Clone")
set1 := NewSet(1, 2, 3)
set1 := New(1, 2, 3)
set2 := set1.Clone()
assert.Equal(true, set1.Size() == set2.Size())
@@ -112,11 +114,11 @@ func TestSet_Delete(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Delete")
set := NewSet[int]()
set := New[int]()
set.Add(1, 2, 3)
set.Delete(3)
assert.Equal(true, set.Equal(NewSet(1, 2)))
assert.Equal(true, set.Equal(New(1, 2)))
}
func TestSet_Equal(t *testing.T) {
@@ -124,9 +126,9 @@ func TestSet_Equal(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Equal")
set1 := NewSet(1, 2, 3)
set2 := NewSet(1, 2, 3)
set3 := NewSet(1, 2, 3, 4)
set1 := New(1, 2, 3)
set2 := New(1, 2, 3)
set3 := New(1, 2, 3, 4)
assert.Equal(true, set1.Equal(set2))
assert.Equal(false, set1.Equal(set3))
@@ -137,7 +139,7 @@ func TestSet_Iterate(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Iterate")
set := NewSet(1, 2, 3)
set := New(1, 2, 3)
arr := []int{}
set.Iterate(func(value int) {
arr = append(arr, value)
@@ -151,7 +153,7 @@ func TestSet_IsEmpty(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_IsEmpty")
set := NewSet[int]()
set := New[int]()
assert.Equal(true, set.IsEmpty())
}
@@ -160,7 +162,7 @@ func TestSet_Size(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Size")
set := NewSet(1, 2, 3)
set := New(1, 2, 3)
assert.Equal(3, set.Size())
}
@@ -169,7 +171,7 @@ func TestSet_Values(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Values")
set := NewSet(1, 2, 3)
set := New(1, 2, 3)
values := set.Values()
assert.Equal(3, len(values))
@@ -180,12 +182,12 @@ func TestSet_Union(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Union")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
set1 := New(1, 2, 3)
set2 := New(2, 3, 4, 5)
unionSet := set1.Union(set2)
assert.Equal(NewSet(1, 2, 3, 4, 5), unionSet)
assert.Equal(New(1, 2, 3, 4, 5), unionSet)
}
func TestSet_Intersection(t *testing.T) {
@@ -193,11 +195,11 @@ func TestSet_Intersection(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Intersection")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
set1 := New(1, 2, 3)
set2 := New(2, 3, 4, 5)
intersectionSet := set1.Intersection(set2)
assert.Equal(NewSet(2, 3), intersectionSet)
assert.Equal(New(2, 3), intersectionSet)
}
func TestSet_SymmetricDifference(t *testing.T) {
@@ -205,10 +207,10 @@ func TestSet_SymmetricDifference(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_SymmetricDifference")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
set1 := New(1, 2, 3)
set2 := New(2, 3, 4, 5)
assert.Equal(NewSet(1, 4, 5), set1.SymmetricDifference(set2))
assert.Equal(New(1, 4, 5), set1.SymmetricDifference(set2))
}
func TestSet_Minus(t *testing.T) {
@@ -216,16 +218,16 @@ func TestSet_Minus(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Minus")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
set3 := NewSet(2, 3)
set1 := New(1, 2, 3)
set2 := New(2, 3, 4, 5)
set3 := New(2, 3)
assert.Equal(NewSet(1), set1.Minus(set2))
assert.Equal(NewSet(4, 5), set2.Minus(set3))
assert.Equal(New(1), set1.Minus(set2))
assert.Equal(New(4, 5), set2.Minus(set3))
}
func TestEachWithBreak(t *testing.T) {
// s := NewSet(1, 2, 3, 4, 5)
// s := New(1, 2, 3, 4, 5)
// var sum int
@@ -241,22 +243,93 @@ func TestEachWithBreak(t *testing.T) {
// assert.Equal(6, sum)
}
// func TestPop(t *testing.T) {
// assert := internal.NewAssert(t, "TestPop")
func TestPop(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Pop")
// s := NewSet[int]()
s := New[int]()
// val, ok := s.Pop()
// assert.Equal(0, val)
// assert.Equal(false, ok)
val, ok := s.Pop()
assert.Equal(0, val)
assert.Equal(false, ok)
// s.Add(1)
// s.Add(2)
// s.Add(3)
s = New(1, 2, 3, 4, 5)
sl := s.ToSlice()
// // s = NewSet(1, 2, 3, 4, 5)
val, ok = s.Pop()
assert.Equal(false, s.Contain(val))
assert.Equal(true, ok)
assert.Equal(len(sl)-1, s.Size())
// val, ok = s.Pop()
// assert.Equal(3, val)
// assert.Equal(true, ok)
// }
var found bool
for _, v := range sl {
if v == val {
found = true
}
}
assert.Equal(true, found)
}
func TestSet_ToSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_ToSlice")
set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1})
set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0})
set3 := New[string]()
slice1 := set1.ToSlice()
slice2 := set2.ToSlice()
slice3 := set3.ToSlice()
sort.Ints(slice1)
sort.Float64s(slice2)
assert.Equal(5, len(slice1))
assert.Equal(4, len(slice2))
assert.Equal(0, len(slice3))
assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7}))
assert.Equal(true, reflect.DeepEqual(slice2, []float64{-2.65, 0, 1.11, 4.25}))
assert.Equal("[]string", reflect.TypeOf(slice3).String())
}
func TestSet_ToSortedSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_ToSortedSlice")
set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1})
set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0})
type Person struct {
Name string
Age int
}
set3 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
slice1 := set1.ToSortedSlice(func(v1, v2 int) bool {
return v1 < v2
})
slice2 := set2.ToSortedSlice(func(v1, v2 float64) bool {
return v2 < v1
})
slice3 := set3.ToSortedSlice(func(v1, v2 Person) bool {
return v1.Age < v2.Age
})
assert.Equal(5, len(slice1))
assert.Equal(4, len(slice2))
assert.Equal(3, len(slice3))
assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7}))
assert.Equal(true, reflect.DeepEqual(slice2, []float64{4.25, 1.11, 0, -2.65}))
assert.Equal(true, reflect.DeepEqual(slice3, []Person{
{"Jerry", 18},
{"Tom", 20},
{"Spike", 25},
}))
}

View File

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

View File

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

View File

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

View File

@@ -40,6 +40,7 @@ export const slugify = (str: string): string =>
export const commonConfig = defineConfig({
title: 'Lancet',
appearance: true,
ignoreDeadLinks: true,
markdown: {
theme: {
@@ -83,7 +84,7 @@ export const commonConfig = defineConfig({
footer: {
copyright: 'Copyright © 2023-present Duke Du',
message: '备案号: 京ICP备2023022770号',
message: '<a href="https://beian.miit.gov.cn/" target="_blank">京ICP备2023022770号-1</a>',
},
},
})

View File

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

View File

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

View File

@@ -43,6 +43,10 @@ import (
- [ToInterface](#ToInterface)
- [Utf8ToGbk](#Utf8ToGbk)
- [GbkToUtf8](#GbkToUtf8)
- [ToStdBase64](#ToStdBase64)
- [ToUrlBase64](#ToUrlBase64)
- [ToRawStdBase64](#ToRawStdBase64)
- [ToRawUrlBase64](#ToRawUrlBase64)
<div STYLE="page-break-after: always;"></div>
@@ -876,3 +880,272 @@ func main() {
// hello
}
```
### <span id="ToStdBase64">ToStdBase64</span>
<p>将值转换为StdBase64编码的字符串。error类型的数据也会把error的原因进行编码复杂的结构会转为JSON格式的字符串</p>
<b>函数签名:</b>
```go
func ToStdBase64(value any) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/_fLJqJD3NMo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
afterEncode := convertor.ToStdBase64(nil)
fmt.Println(afterEncode)
afterEncode = convertor.ToStdBase64("")
fmt.Println(afterEncode)
stringVal := "hello"
afterEncode = convertor.ToStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToStdBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToStdBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToStdBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
//
//
// aGVsbG8=
// aGVsbG8=
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
// MTIzLjQ1Ng==
// dHJ1ZQ==
// ZXJy
}
```
### <span id="ToUrlBase64">ToUrlBase64</span>
<p>值转换为 ToUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
<b>函数签名:</b>
```go
func ToUrlBase64(value any) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/C_d0GlvEeUR)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
afterEncode := convertor.ToUrlBase64(nil)
fmt.Println(afterEncode)
stringVal := "hello"
afterEncode = convertor.ToUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToUrlBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToUrlBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToUrlBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToUrlBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
//
// aGVsbG8=
// aGVsbG8=
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
// MTIzLjQ1Ng==
// dHJ1ZQ==
// ZXJy
}
```
### <span id="ToRawStdBase64">ToRawStdBase64</span>
<p>值转换为 ToRawStdBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
<b>函数签名:</b>
```go
func ToRawStdBase64(value any) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wSAr3sfkDcv)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
stringVal := "hello"
afterEncode = convertor.ToRawStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToRawStdBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToRawStdBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode := convertor.ToRawStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToRawStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
```
### <span id="ToRawUrlBase64">ToRawUrlBase64</span>
<p>值转换为 ToRawUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
<b>函数签名:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/HwdDPFcza1O)</span></b>
```go
func ToRawUrlBase64(value any) string
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
stringVal := "hello"
afterEncode := convertor.ToRawUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToRawUrlBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToRawUrlBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToRawUrlBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToRawUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
```

View File

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

View File

@@ -26,6 +26,8 @@ import (
- [Remove](#Remove)
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [IndexOfFunc](#IndexOfFunc)
- [LastIndexOfFunc](#LastIndexOfFunc)
- [IsEmpty](#IsEmpty)
- [Contain](#Contain)
- [ValueOf](#ValueOf)
@@ -198,6 +200,58 @@ func main() {
```
### <span id="IndexOfFunc">IndexOfFunc</span>
<p>返回第一个满足判断函数f(v)的元素的索引,如果找不到则返回-1。</p>
<b>函数签名:</b>
```go
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1, 2, 3})
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 1 })) //0
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 0 })) //-1
}
```
### <span id="LastIndexOfFunc">LastIndexOfFunc</span>
<p>返回最后一个满足判断函数f(v)的元素的索引,如果找不到则返回-1。</p>
<b>函数签名:</b>
```go
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1, 2, 3, 1})
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1
}
```
### IsEmpty
如果此列表不包含任何元素,则返回 true。

View File

@@ -31,6 +31,7 @@ import (
- [Iterate](#Iterate)
- [Keys](#Keys)
- [Values](#Values)
- [FilterByValue](#FilterByValue)
<div STYLE="page-break-after: always;"></div>
@@ -276,7 +277,7 @@ func main() {
### <span id="Values">Values</span>
<p>返回hashmap所有值的切片 (随机顺序).</p>
<p>返回hashmap所有值的切片 (随机顺序)</p>
<b>函数签名:</b>
@@ -306,3 +307,40 @@ func main() {
```
### <span id="FilterByValue">FilterByValue</span>
<p>返回一个过滤后的HashMap。 如果任何值与 perdicate 函数不匹配,则返回 nil否则返回包含选定值的 HashMap。</p>
<b>函数签名:</b>
```go
func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := hashmap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
hm.Put("d", 4)
hm.Put("e", 5)
hm.Put("f", 6)
filteredHM := hm.FilterByValue(func(value any) bool {
return value.(int) == 1 || value.(int) == 3
})
fmt.Println(filteredHM.Size()) //2
}
```

View File

@@ -0,0 +1,412 @@
# Optional
Optional类型代表一个可选的值它要么包含一个实际值要么为空。
<div STYLE="page-break-after: always;"></div>
## 源码
- [https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go](https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go)
<div STYLE="page-break-after: always;"></div>
## 用法
```go
import (
"github.com/duke-git/lancet/v2/datastructure/optional"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [Of](#Of)
- [FromNillable](#FromNillable)
- [Default](#Default)
- [IsNotNil](#IsNotNil)
- [IsNil](#IsNil)
- [IsNotNil](#IsNotNil)
- [IfNotNilOrElse](#IfNotNilOrElse)
- [Umwarp](#Umwarp)
- [OrElse](#OrElse)
- [OrElseGet](#OrElseGet)
- [OrElseTrigger](#OrElseTrigger)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="Of">Of</span>
<p>返回一个包含非空值的Optional。</p>
<b>函数签名:</b>
```go
func Of[T any](value T) Optional[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
value := 42
opt := optional.Of(value)
fmt.Println(opt.Get())
// Output:
// 42
}
```
### <span id="FromNillable">FromNillable</span>
<p>返回一个包含给定值的Optional该值可能为空 (nil)。</p>
<b>函数签名:</b>
```go
func FromNillable[T any](value *T) Optional[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
var value *int = nil
opt := optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
value = new(int)
*value = 42
opt = optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
// Output:
// false
// true
}
```
### <span id="Default">Default</span>
<p>返回一个空Optional实例。</p>
<b>函数签名:</b>
```go
func Default[T any]() Optional[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
fmt.Println(optDefault.IsNil())
// Output:
// true
}
```
### <span id="IsNil">IsNil</span>
<p>验证Optional是否为空。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) IsNil() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
fmt.Println(optDefault.IsNil())
// Output:
// true
}
```
### <span id="IsNotNil">IsNotNil</span>
<p>检查当前Optional内是否存在值。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) IsNotNil() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
var value *int = nil
opt := optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
value = new(int)
*value = 42
opt = optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
// Output:
// false
// true
}
```
### <span id="IfNotNil">IfNotNil</span>
<p>如果值存在则使用action方法执行给定的操作。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) IfNotNil(action func(value T))
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
called := false
action := func(value int) { called = true }
optDefault := optional.Default[int]()
optDefault.IfNotNil(action)
fmt.Println(called)
called = false // Reset for next test
optWithValue := optional.Of(42)
optWithValue.IfNotNil(action)
fmt.Println(optWithValue.IsNotNil())
// Output:
// false
// true
}
```
### <span id="IfNotNilOrElse">IfNotNilOrElse</span>
<p>根据是否存在值执行相应的操作:有值则执行指定操作,没有值则执行默认操作。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func())
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
calledWithValue := false
valueAction := func(value int) { calledWithValue = true }
emptyAction := func() { t.Errorf("Empty action should not be called when value is present") }
optWithValue := optional.Of(42)
optWithValue.IfNotNilOrElse(valueAction, emptyAction)
fmt.Println(calledWithValue)
calledWithEmpty := false
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
emptyAction = func() { calledWithEmpty = true }
optDefault := optional.Default[int]()
optDefault.IfNotNilOrElse(valueAction, emptyAction)
fmt.Println(calledWithEmpty)
// Output:
// true
// true
}
```
### <span id="Unwrap">Unwrap</span>
<p>如果存在返回该值否则引发panic。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) Unwrap() T
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
value := 42
opt := optional.Of(value)
fmt.Println(opt.Unwrap())
// Output:
// 42
}
```
### <span id="OrElse">OrElse</span>
<p>检查Optional值是否存在如果存在则直接返回该值。如果不存在返回参数other值。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) OrElse(other T) T
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Empty[int]()
val := optDefault.OrElse(100)
fmt.Println(val)
optWithValue := optional.Of(42)
val = optWithValue.OrElse(100)
fmt.Println(val)
// Output:
// 100
// 42
}
```
### <span id="OrElseGet">OrElseGet</span>
<p>检查Optional值是否存在如果存在则直接返回该值。如果不存在则调用一个提供的函数 (supplier),并返回该函数的执行结果。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) OrElseGet(action func() T) T
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
action := func() int { return 100 }
val := optDefault.OrElseGet(action)
fmt.Println(val)
// Output:
// 100
}
```
### <span id="OrElseTrigger">OrElseTrigger</span>
<p>检查Optional值是否存在如果存在则直接返回该值否则返回错误。</p>
<b>函数签名:</b>
```go
OrElseTrigger(errorHandler func() error) (T, error)
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
fmt.Println(err.Error())
optWithValue := optional.Of(42)
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
fmt.Println(val)
fmt.Println(err)
// Output:
// no value
// 42
// nil
}
```

View File

@@ -1,6 +1,6 @@
# Set
Set 集合数据结构类似列表。Set 中元素不重复。
集合数据结构类似列表。Set中元素不重复。
<div STYLE="page-break-after: always;"></div>
@@ -22,9 +22,9 @@ import (
## 目录
- [NewSet](#NewSet)
- [NewSetFromSlice](#NewSetFromSlice)
- [Values](#Values)
- [New](#New)
- [FromSlice](#FromSlice)
- [Values<sup>deprecated</sup>](#Values)
- [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
- [AddIfNotExistBy](#AddIfNotExistBy)
@@ -40,20 +40,23 @@ import (
- [Intersection](#Intersection)
- [SymmetricDifference](#SymmetricDifference)
- [Minus](#Minus)
- [Pop](#Pop)
- [ToSlice](#ToSlice)
- [ToSortedSlice](#ToSortedSlice)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="NewSet">NewSet</span>
### <span id="New">New</span>
<p>返回Set结构体对象</p>
<b>函数签名:</b>
```go
type Set[T comparable] map[T]bool
func NewSet[T comparable](items ...T) Set[T]
type Set[T comparable] map[T]struct{}
func New[T comparable](items ...T) Set[T]
```
<b>示例:</b>
@@ -67,19 +70,19 @@ import (
)
func main() {
st := set.NewSet[int](1,2,2,3)
st := set.New[int](1,2,2,3)
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="NewSetFromSlice">NewSetFromSlice</span>
### <span id="FromSlice">FromSlice</span>
<p>基于切片创建集合</p>
<b>函数签名:</b>
```go
func NewSetFromSlice[T comparable](items []T) Set[T]
func FromSlice[T comparable](items []T) Set[T]
```
<b>示例:</b>
@@ -93,14 +96,16 @@ import (
)
func main() {
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
st := set.FromSlice([]int{1, 2, 2, 3})
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="Values">Values</span>
<p>获取集合中所有元素的切片</p>
<p>获取集合中所有元素的切片</p>
> ⚠️ 本函数已弃用,使用`ToSlice`代替。
<b>函数签名:</b>
@@ -119,7 +124,7 @@ import (
)
func main() {
st := set.NewSet[int](1,2,2,3)
st := set.New[int](1,2,2,3)
fmt.Println(st.Values()) //1,2,3
}
```
@@ -145,7 +150,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
fmt.Println(st.Values()) //1,2,3
@@ -173,7 +178,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
r1 := st.AddIfNotExist(1)
@@ -206,7 +211,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2)
ok := st.AddIfNotExistBy(3, func(val int) bool {
@@ -245,7 +250,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
set.Delete(3)
@@ -274,7 +279,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
fmt.Println(st.Contain(1)) //true
@@ -303,9 +308,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(1, 2)
set3 := set.NewSet(1, 2, 3, 4)
set1 := set.New(1, 2, 3)
set2 := set.New(1, 2)
set3 := set.New(1, 2, 3, 4)
fmt.Println(set1.ContainAll(set2)) //true
fmt.Println(set1.ContainAll(set3)) //false
@@ -333,7 +338,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
fmt.Println(set1.Size()) //3
}
@@ -360,7 +365,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
set2 := set1.Clone()
fmt.Println(set1.Size() == set2.Size()) //true
@@ -389,9 +394,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(1, 2, 3)
set3 := set.NewSet(1, 2, 3, 4)
set1 := set.New(1, 2, 3)
set2 := set.New(1, 2, 3)
set3 := set.New(1, 2, 3, 4)
fmt.Println(set1.Equal(set2)) //true
fmt.Println(set1.Equal(set3)) //false
@@ -419,7 +424,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
arr := []int{}
set.Iterate(func(item int) {
arr = append(arr, item)
@@ -450,7 +455,7 @@ import (
)
func main() {
s := set.NewSet(1, 2, 3, 4, 5)
s := set.New(1, 2, 3, 4, 5)
var sum int
@@ -487,8 +492,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet()
set1 := set.New(1, 2, 3)
set2 := set.New()
fmt.Println(set1.IsEmpty()) //false
fmt.Println(set2.IsEmpty()) //true
@@ -516,8 +521,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.Union(set2)
fmt.Println(set3.Values()) //1,2,3,4,5
@@ -545,8 +550,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.Intersection(set2)
fmt.Println(set3.Values()) //2,3
@@ -574,8 +579,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.SymmetricDifference(set2)
fmt.Println(set3.Values()) //1,4,5
@@ -603,9 +608,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set3 := set.NewSet(2, 3)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set.New(2, 3)
res1 := set1.Minus(set2)
fmt.Println(res1.Values()) //1
@@ -636,7 +641,7 @@ import (
)
func main() {
s := set.NewSet[int]()
s := set.New[int]()
s.Add(1)
s.Add(2)
s.Add(3)
@@ -647,3 +652,58 @@ func main() {
fmt.Println(ok) // true
}
```
### <span id="ToSlice">ToSlice</span>
<p>以切片的形式返回集合中所有的元素(无序)</p>
<b>函数签名:</b>
```go
func (s Set[T]) ToSlice() (v T, ok bool)
```
<b>示例:</b>
```go
func main() {
s := set.New(1, 2, 3, 4, 5)
val := s.ToSlice()
fmt.Println(val) // [2 3 4 5 1]
}
```
### <span id="ToSortedSlice">ToSortedSlice</span>
<p>以切片的形式返回集合中所有的元素(按给定的规则排序)</p>
<b>函数签名:</b>
```go
func (s Set[T]) ToSortedSlice() (v T, ok bool)
```
<b>示例:</b>
```go
func main() {
s1 := set.New(1, 2, 3, 4, 5)
type Person struct {
Name string
Age int
}
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
return v1 < v2
})
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
return v1.Age < v2.Age
})
fmt.Println(res1) // [1 2 3 4 5]
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
}
```

View File

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

View File

@@ -26,6 +26,7 @@ import (
- [CreateFile](#CreateFile)
- [CreateDir](#CreateDir)
- [CopyFile](#CopyFile)
- [CopyDir](#CopyDir)
- [CurrentPath](#CurrentPath)
- [FileMode](#FileMode)
- [MiMeType](#MiMeType)
@@ -49,6 +50,8 @@ import (
- [WriteStringToFile](#WriteStringToFile)
- [WriteBytesToFile](#WriteBytesToFile)
- [ReadFile](#ReadFile)
- [ChunkRead](#ChunkRead)
- [ParallelChunkRead](#ParallelChunkRead)
<div STYLE="page-break-after: always;"></div>
@@ -162,6 +165,34 @@ func main() {
}
```
### <span id="CopyDir">CopyDir</span>
<p>拷贝文件夹到目标路径会递归复制文件夹下所有的文件及文件夹并且访问权限也与源文件夹保持一致。当dstPath存在时会返回error</p>
<b>函数签名:</b>
```go
func CopyDir(srcPath string, dstPath string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/YAyFTA_UuPb)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.CopyFile("./test_src", "./test_dest")
if err != nil {
fmt.Println(err)
}
}
```
### <span id="CurrentPath">CurrentPath</span>
<p>返回当前位置的绝对路径。</p>
@@ -759,7 +790,7 @@ func main() {
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/umAIomZFV1c)</span></b>
```go
package main
@@ -782,7 +813,7 @@ func main() {
}
headers := []string{"Name", "Age", "Gender"}
err := WriteMapsToCsv(csvFilePath, records, false, ';', headers)
err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers)
if err != nil {
log.Fatal(err)
@@ -926,9 +957,123 @@ func main() {
if err != nil {
return
}
fmt.Println(string(dat))
// Output:
// User-agent: *
// Disallow: /deny
}
```
### <span id="ChunkRead">ChunkRead</span>
<p>从文件的指定偏移读取块并返回块内所有行。</p>
<b>函数签名:</b>
```go
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/r0hPmKWhsgf)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100
// test1.csv file content:
// Lili,22,female
// Jim,21,male
filePath := "./testdata/test1.csv" // 替换为你的文件路径
f, err := os.Open(filePath)
if err != nil {
return
}
defer f.Close()
var bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultChunkSizeMB*mb)
},
}
lines, err := fileutil.ChunkRead(f, 0, 100, &bufPool)
if err != nil {
return
}
fmt.Println(lines[0])
fmt.Println(lines[1])
// Output:
// Lili,22,female
// Jim,21,male
}
```
### <span id="ParallelChunkRead">ParallelChunkRead</span>
<p>并行读取文件并将每个块的行发送到指定通道。</p>
<b>函数签名:</b>
```go
// filePath:文件路径
// chunkSizeMB: 分块的大小单位MB设置为0时使用默认100MB,设置过大反而不利,视情调整
// maxGoroutine: 并发读取分块的数量设置为0时使用CPU核心数
// linesCh: 用于接收返回结果的通道。
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/teMXnCsdSEw)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
// test1.csv file content:
// Lili,22,female
// Jim,21,male
filePath := "./testdata/test1.csv"
go fileutil.ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
var totalLines int
for lines := range linesCh {
totalLines += len(lines)
for _, line := range lines {
fmt.Println(line)
}
}
fmt.Println(totalLines)
// Output:
// Lili,22,female
// Jim,21,male
// 2
}
```

View File

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

View File

@@ -7,6 +7,7 @@ function 函数包控制函数执行流程,包含部分函数式编程。
## 源码:
- [https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go)
- [https://github.com/duke-git/lancet/blob/main/function/predicate.go](https://github.com/duke-git/lancet/blob/main/function/predicate.go)
- [https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go)
<div STYLE="page-break-after: always;"></div>
@@ -27,11 +28,21 @@ import (
- [Before](#Before)
- [CurryFn](#CurryFn)
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Debounce](#Debounce)
- [Debounced<sup>deprecated</sup>](#Debounced)
- [Delay](#Delay)
- [Schedule](#Schedule)
- [Pipeline](#Pipeline)
- [Watcher](#Watcher)
- [And](#And)
- [Or](#Or)
- [Negate](#Negate)
- [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand)
- [AcceptIf](#AcceptIf)
- [Throttle](#Throttle)
<div STYLE="page-break-after: always;"></div>
@@ -184,9 +195,58 @@ func main() {
}
```
### <span id="Debounce">Debounce</span>
<p>创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。</p>
<b>函数签名:</b>
```go
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
```
### <span id="Debounced">Debounced</span>
<p>创建一个debounced函数该函数延迟调用fn直到自上次调用debounced函数后等待持续时间过去。</p>
<p>创建一个函数的去抖动版本。</p>
> ⚠️ 本函数已弃用. 使用 `Debounce` 代替.
<b>函数签名:</b>
@@ -404,3 +464,323 @@ func longRunningTask() {
}
```
### <span id="And">And</span>
<p>返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑and操作。只有当所有谓词判断函数对于给定的值都返回true时返回true, 否则返回false。</p>
<b>函数签名:</b>
```go
func And[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dTBHJMQ0zD2)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isNumericAndLength5 := function.And(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcde"))
// Output:
// true
// false
// false
}
```
### <span id="Or">Or</span>
<p>返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑or操作。只有当所有谓词判断函数对于给定的值都返回false时返回false, 否则返回true。</p>
<b>函数签名:</b>
```go
func Or[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/LitCIsDFNDA)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
containsDigitOrSpecialChar := function.Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
)
fmt.Println(containsDigitOrSpecialChar("hello!"))
fmt.Println(containsDigitOrSpecialChar("hello"))
// Output:
// true
// false
}
```
### <span id="Negate">Negate</span>
<p>返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。</p>
<b>函数签名:</b>
```go
func Negate[T any](predicate func(T) bool) func(T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jbI8BtgFnVE)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
// Define some simple predicates for demonstration
isUpperCase := func(s string) bool {
return strings.ToUpper(s) == s
}
isLowerCase := func(s string) bool {
return strings.ToLower(s) == s
}
isMixedCase := function.Negate(function.Or(isUpperCase, isLowerCase))
fmt.Println(isMixedCase("ABC"))
fmt.Println(isMixedCase("AbC"))
// Output:
// false
// true
}
```
### <span id="Nor">Nor</span>
<p>返回一个组合谓词函数,表示给定值上所有谓词逻辑非或 (nor) 的结果。只有当所有谓词函数对给定值都返回false时该组合谓词函数才返回true。</p>
<b>函数签名:</b>
```go
func Nor[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/2KdCoBEOq84)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
match := function.Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("dbcdckkeee"))
match = function.Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("0123456789"))
// Output:
// true
// false
}
```
### <span id="Nand">Nand</span>
<p>返回一个复合谓词函数,表示给定谓词函数列表的逻辑非与 (NAND)。仅当列表中所有函数对给定参数返回false时才返回true否则返回false。</p>
<b>函数签名:</b>
```go
func Nand[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Rb-FdNGpgSO)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isNumericAndLength5 := function.Nand(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcdef"))
// Output:
// false
// false
// true
}
```
### <span id="Xnor">Xnor</span>
<p>返回一个复合谓词函数,表示给定一组谓词函数的逻辑异或 (XNOR)。只有当所有 谓词函数对给参数都返回true或false时该谓词函数才返回true。</p>
<b>函数签名:</b>
```go
func Xnor[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/FJxko8SFbqc)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isEven := func(i int) bool { return i%2 == 0 }
isPositive := func(i int) bool { return i > 0 }
match := function.Xnor(isEven, isPositive)
fmt.Println(match(2))
fmt.Println(match(-3))
fmt.Println(match(3))
// Output:
// true
// true
// false
}
```
### <span id="AcceptIf">AcceptIf</span>
<p>AcceptIf函数会返回另一个函数该函数的签名与 apply 函数相同,但同时还会包含一个布尔值来表示成功或失败。</p>
<b>函数签名:</b>
```go
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/XlXHHtzCf7d)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
adder := function.AcceptIf(
function.And(
func(x int) bool {
return x > 10
}, func(x int) bool {
return x%2 == 0
}),
func(x int) int {
return x + 1
},
)
result, ok := adder(20)
fmt.Println(result)
fmt.Println(ok)
result, ok = adder(21)
fmt.Println(result)
fmt.Println(ok)
// Output:
// 21
// true
// 0
// false
}
```
### <span id="Throttle">Throttle</span>
<p>创建一个函数的节流版本。返回的函数保证在每个时间间隔内最多只会被调用一次。</p>
<b>函数签名:</b>
```go
func Throttle(fn func(), interval time.Duration) func()
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
throttledFn := function.Throttle(fn, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
}
```

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,10 @@ import (
- [RoundToFloat](#RoundToFloat)
- [RoundToString](#RoundToString)
- [TruncRound](#TruncRound)
- [CeilToFloat](#CeilToFloat)
- [CeilToString](#CeilToString)
- [FloorToFloat](#FloorToFloat)
- [FloorToString](#FloorToString)
- [Range](#Range)
- [RangeWithStep](#RangeWithStep)
- [AngleToRadian](#AngleToRadian)
@@ -47,6 +51,7 @@ import (
- [Log](#Log)
- [Sum](#Sum)
- [Abs](#Abs)
- [Div](#Div)
<div STYLE="page-break-after: always;"></div>
@@ -392,7 +397,7 @@ func main() {
<b>函数签名:</b>
```go
func RoundToFloat(x float64, n int) float64
func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ghyb528JRJL)</span></b>
@@ -428,7 +433,7 @@ func main() {
<b>函数签名:</b>
```go
func RoundToString(x float64, n int) string
func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/kZwpBRAcllO)</span></b>
@@ -464,7 +469,7 @@ func main() {
<b>函数签名:</b>
```go
func TruncRound(x float64, n int) float64
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/aumarSHIGzP)</span></b>
@@ -493,6 +498,150 @@ func main() {
}
```
### <span id="CeilToFloat">CeilToFloat</span>
<p>向上舍入进一法保留n位小数。</p>
<b>函数签名:</b>
```go
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/8hOeSADZPCo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.CeilToFloat(3.14159, 1)
result2 := mathutil.CeilToFloat(3.14159, 2)
result3 := mathutil.CeilToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5
}
```
### <span id="CeilToString">CeilToString</span>
<p>向上舍入进一法保留n位小数返回字符串。</p>
<b>函数签名:</b>
```go
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wy5bYEyUKKG)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.CeilToString(3.14159, 1)
result2 := mathutil.CeilToString(3.14159, 2)
result3 := mathutil.CeilToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5.0000
}
```
### <span id="FloorToFloat">FloorToFloat</span>
<p>向下舍入去尾法保留n位小数。</p>
<b>函数签名:</b>
```go
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vbCBrQHZEED)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.FloorToFloat(3.14159, 1)
result2 := mathutil.FloorToFloat(3.14159, 2)
result3 := mathutil.FloorToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5
}
```
### <span id="FloorToString">FloorToString</span>
<p>向下舍入去尾法保留n位小数返回字符串。</p>
<b>函数签名:</b>
```go
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Qk9KPd2IdDb)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.FloorToString(3.14159, 1)
result2 := mathutil.FloorToString(3.14159, 2)
result3 := mathutil.FloorToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5.0000
}
```
### <span id="Range">Range</span>
<p>根据指定的起始值和数量,创建一个数字切片。</p>
@@ -965,16 +1114,52 @@ import (
func main() {
result1 := Abs(-1)
result2 := Abs(-0.1)
result3 := Abs(float32(0.2))
result2 := Abs(-0.1)
result3 := Abs(float32(0.2))
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 1
// 0.1
// 0.2
// Output:
// 1
// 0.1
// 0.2
}
```
### <span id="Div">Div</span>
<p>除法运算。</p>
<b>函数签名:</b>
```go
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/WLxDdGXXYat)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Div(9, 4)
result2 := mathutil.Div(1, 2)
result3 := mathutil.Div(0, 666)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 2.25
// 0.5
// 0
}
```

View File

@@ -624,7 +624,9 @@ func main() {
### <span id="HttpGet">HttpGet</span>
<p>发送http get请求。(已废弃:使用SendRequest)</p>
<p>发送http get请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -666,7 +668,9 @@ func main() {
### <span id="HttpPost">HttpPost</span>
<p>发送http post请求。(已废弃:使用SendRequest)</p>
<p>发送http post请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -713,7 +717,9 @@ func main() {
### <span id="HttpPut">HttpPut</span>
<p>发送http put请求。(已废弃:使用SendRequest)</p>
<p>发送http put请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -763,7 +769,9 @@ func main() {
### <span id="HttpDelete">HttpDelete</span>
<p>发送http delete请求。(已废弃:使用SendRequest)</p>
<p>发送http delete请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -802,7 +810,9 @@ func main() {
### <span id="HttpPatch">HttpPatch</span>
<p>发送http patch请求。(已废弃:使用SendRequest)</p>
<p>发送http patch请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>

View File

@@ -25,8 +25,8 @@ import (
- [Of](#Of)
- [Unwrap](#Unwrap)
- [ExtractPointer](#ExtractPointer)
- [UnwarpOr](#UnwarpOr)
- [UnwarpOrDefault](#UnwarpOrDefault)
- [UnwrapOr](#UnwrapOr)
- [UnwrapOrDefault](#UnwrapOrDefault)
<div STYLE="page-break-after: always;"></div>
@@ -136,14 +136,14 @@ func main() {
}
```
### <span id="UnwarpOr">UnwarpOr</span>
### <span id="UnwrapOr">UnwrapOr</span>
<p>返回指针的值如果指针为零值则返回fallback。</p>
<b>函数签名:</b>
```go
UnwarpOr[T any](p *T, fallback T) T
UnwrapOr[T any](p *T, fallback T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/mmNaLC38W8C)</span></b>
@@ -163,10 +163,10 @@ func main() {
var c *int
var d *string
result1 := pointer.UnwarpOr(&a, 456)
result2 := pointer.UnwarpOr(&b, "abc")
result3 := pointer.UnwarpOr(c, 456)
result4 := pointer.UnwarpOr(d, "def")
result1 := pointer.UnwrapOr(&a, 456)
result2 := pointer.UnwrapOr(&b, "abc")
result3 := pointer.UnwrapOr(c, 456)
result4 := pointer.UnwrapOr(d, "def")
fmt.Println(result1)
fmt.Println(result2)
@@ -181,14 +181,14 @@ func main() {
}
```
### <span id="UnwarpOrDefault">UnwarpOrDefault</span>
### <span id="UnwrapOrDefault">UnwrapOrDefault</span>
<p>返回指针的值,如果指针为零值,则返回相应零值。</p>
<b>函数签名:</b>
```go
UnwarpOrDefault[T any](p *T) T
UnwrapOrDefault[T any](p *T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
@@ -208,10 +208,10 @@ func main() {
var c *int
var d *string
result1 := pointer.UnwarpOrDefault(&a)
result2 := pointer.UnwarpOrDefault(&b)
result3 := pointer.UnwarpOrDefault(c)
result4 := pointer.UnwarpOrDefault(d)
result1 := pointer.UnwrapOrDefault(&a)
result2 := pointer.UnwrapOrDefault(&b)
result3 := pointer.UnwrapOrDefault(c)
result4 := pointer.UnwrapOrDefault(d)
fmt.Println(result1)
fmt.Println(result2)

View File

@@ -25,15 +25,22 @@ import (
- [RandBytes](#RandBytes)
- [RandInt](#RandInt)
- [RandString](#RandString)
- [RandFromGivenSlice](#RandFromGivenSlice)
- [RandSliceFromGivenSlice](#RandSliceFromGivenSlice)
- [RandUpper](#RandUpper)
- [RandLower](#RandLower)
- [RandNumeral](#RandNumeral)
- [RandNumeralOrLetter](#RandNumeralOrLetter)
- [RandSymbolChar](#RandSymbolChar)
- [UUIdV4](#UUIdV4)
- [RandIntSlice](#RandIntSlice)
- [RandUniqueIntSlice](#RandUniqueIntSlice)
- [RandFloat](#RandFloat)
- [RandFloats](#RandFloats)
- [RandStringSlice](#RandStringSlice)
- [RandBool](#RandBool)
- [RandBoolSlice](#RandBoolSlice)
<div STYLE="page-break-after: always;"></div>
@@ -117,6 +124,60 @@ func main() {
}
```
### <span id="RandFromGivenSlice">RandFromGivenSlice</span>
<p>从给定切片中随机生成元素。</p>
<b>函数签名:</b>
```go
func RandFromGivenSlice[T any](slice []T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
nicknames := []string{"张三", "李四", "王五", "赵六", "钱七"}
randElm := random.RandFromGivenSlice(nicknames)
fmt.Println(randElm)
}
```
### <span id="RandSliceFromGivenSlice">RandSliceFromGivenSlice</span>
<p>从给定切片中生成长度为 num 的随机切片。</p>
<b>函数签名:</b>
```go
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon","mango", "nectarine", "orange"}
chosen3goods := random.RandSliceFromGivenSlice(goods, 3, false)
fmt.Println(chosen3goods)
}
```
### <span id="RandUpper">RandUpper</span>
<p>生成给定长度的随机大写字母字符串</p>
@@ -276,14 +337,40 @@ func main() {
}
```
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
### <span id="RandIntSlice">RandIntSlice</span>
<p>生成一个不重复的长度为n的随机int切片。</p>
<p>生成一个特定长度的随机int切片数值范围[min, max)。</p>
<b>函数签名:</b>
```go
func RandUniqueIntSlice(n, min, max int) []int
func RandIntSlice(length, min, max int) []int
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
result := random.RandIntSlice(5, 0, 10)
fmt.Println(result) //[1 2 7 1 5] (random)
}
```
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
<p>生成一个特定长度的数值不重复的随机int切片数值范围[min, max)。</p>
<b>函数签名:</b>
```go
func RandUniqueIntSlice(length, min, max int) []int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uBkRSOz73Ec)</span></b>
@@ -304,7 +391,7 @@ func main() {
### <span id="RandFloat">RandFloat</span>
<p>生成随机float64数,可以指定范围和精度。</p>
<p>生成一个随机float64数,可以指定精度。数值范围[min, max)。</p>
<b>函数签名:</b>
@@ -330,7 +417,7 @@ func main() {
### <span id="RandFloats">RandFloats</span>
<p>生成随机float64数字切片,指定长度,范围和精度.</p>
<p>生成一个特定长度的随机float64切片可以指定数值精度。数值范围[min, max)。</p>
<b>函数签名:</b>
@@ -352,4 +439,85 @@ func main() {
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
fmt.Println(floatNumber) //[3.42 3.99 1.3 2.38 4.23] (random)
}
```
### <span id="RandStringSlice">RandStringSlice</span>
<p>生成随机字符串slice. 字符串类型需要是以下几种或者它们的组合: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars。</p>
<b>函数签名:</b>
```go
func RandStringSlice(charset string, sliceLen, strLen int) []string
```
<b>实例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
strs := random.RandStringSlice(random.Letters, 4, 6)
fmt.Println(strs)
// output random string slice like below:
//[CooSMq RUFjDz FAeMPf heRyGv]
}
```
### <span id="RandBool">RandBool</span>
<p>生成随机bool值(true or false)。</p>
<b>函数签名:</b>
```go
func RandBool() bool
```
<b>实例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
result := random.RandBool()
fmt.Println(result) // true or false (random)
}
```
### <span id="RandBoolSlice">RandBoolSlice</span>
<p>生成特定长度的随机bool slice。</p>
<b>函数签名:</b>
```go
func RandBoolSlice(length int) []bool
```
<b>实例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
result := random.RandBoolSlice(2)
fmt.Println(result) // [true false] (random)
}
```

View File

@@ -27,6 +27,10 @@ import (
- [RetryFunc](#RetryFunc)
- [RetryDuration](#RetryDuration)
- [RetryTimes](#RetryTimes)
- [BackoffStrategy](#BackoffStrategy)
- [RetryWithCustomBackoff](#RetryWithCustomBackoff)
- [RetryWithLinearBackoff](#RetryWithLinearBackoff)
- [RetryWithExponentialWithJitterBackoff](#RetryWithExponentialWithJitterBackoff)
<div STYLE="page-break-after: always;"></div>
@@ -66,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,
@@ -112,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 {
@@ -169,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>
@@ -247,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 {
@@ -260,3 +218,201 @@ func main() {
// 3
}
```
### <span id="BackoffStrategy">BackoffStrategy</span>
<p>定义计算退避间隔的方法的接口。</p>
<b>函数签名:</b>
```go
// BackoffStrategy is an interface that defines a method for calculating backoff intervals.
type BackoffStrategy interface {
// CalculateInterval returns the time.Duration after which the next retry attempt should be made.
CalculateInterval() time.Duration
}
```
<b>示例:</b>
```go
package main
import (
"fmt"
"errors"
"log"
"github.com/duke-git/lancet/v2/retry"
)
type ExampleCustomBackoffStrategy struct {
interval time.Duration
}
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
return c.interval + 1
}
func main() {
number := 0
increaseNumber := func() error {
number++
if number == 3 {
return nil
}
return errors.New("error occurs")
}
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&示例CustomBackoffStrategy{interval: time.Microsecond * 50}))
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
```
### <span id="RetryWithCustomBackoff">RetryWithCustomBackoff</span>
<p>设置自定义退避策略。</p>
<b>函数签名:</b>
```go
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jIm_o2vb5Y4)</span></b>
```go
package main
import (
"fmt"
"errors"
"log"
"github.com/duke-git/lancet/v2/retry"
)
type ExampleCustomBackoffStrategy struct {
interval time.Duration
}
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
return c.interval + 1
}
func main() {
number := 0
increaseNumber := func() error {
number++
if number == 3 {
return nil
}
return errors.New("error occurs")
}
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&示例CustomBackoffStrategy{interval: time.Microsecond * 50}))
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
```
### <span id="RetryWithLinearBackoff">RetryWithLinearBackoff</span>
<p>设置线性策略退避。</p>
<b>函数签名:</b>
```go
func RetryWithLinearBackoff(interval time.Duration) Option
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/PDet2ZQZwcB)</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")
}
err := retry.Retry(increaseNumber, retry.RetryWithLinearBackoff(time.Microsecond*50))
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
```
### <span id="RetryWithExponentialWithJitterBackoff">RetryWithExponentialWithJitterBackoff</span>
<p>设置指数策略退避。</p>
<b>函数签名:</b>
```go
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/xp1avQmn16X)</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")
}
err := retry.Retry(increaseNumber, retry.RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
```

View File

@@ -6,7 +6,8 @@ slice 包包含操作切片的方法集合。
## 源码:
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go](https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go)
<div STYLE="page-break-after: always;"></div>
@@ -44,6 +45,7 @@ import (
- [Equal](#Equal)
- [EqualWith](#EqualWith)
- [Filter](#Filter)
- [FilterConcurrent](#FilterConcurrent)
- [Find<sup>deprecated</sup>](#Find)
- [FindBy](#FindBy)
- [FindLast<sup>deprecated</sup>](#FindLast)
@@ -51,6 +53,7 @@ import (
- [Flatten](#Flatten)
- [FlattenDeep](#FlattenDeep)
- [ForEach](#ForEach)
- [ForEachConcurrent](#ForEachConcurrent)
- [ForEachWithBreak](#ForEachWithBreak)
- [GroupBy](#GroupBy)
- [GroupWith](#GroupWith)
@@ -61,11 +64,13 @@ import (
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [Map](#Map)
- [MapConcurrent](#MapConcurrent)
- [FilterMap](#FilterMap)
- [FlatMap](#FlatMap)
- [Merge](#Merge)
- [Reverse](#Reverse)
- [Reduce<sup>deprecated</sup>](#Reduce)
- [ReduceConcurrent](#ReduceConcurrent)
- [ReduceBy](#ReduceBy)
- [ReduceRight](#ReduceRight)
- [Replace](#Replace)
@@ -86,6 +91,9 @@ import (
- [ToSlicePointer](#ToSlicePointer)
- [Unique](#Unique)
- [UniqueBy](#UniqueBy)
- [UniqueByComparator](#UniqueByComparator)
- [UniqueByField](#UniqueByField)
- [UniqueByConcurrent](#UniqueByConcurrent)
- [Union](#Union)
- [UnionBy](#UnionBy)
- [UpdateAt](#UpdateAt)
@@ -93,6 +101,11 @@ import (
- [KeyBy](#KeyBy)
- [Join](#Join)
- [Partition](#Partition)
- [SetToDefaultIf](#SetToDefaultIf)
- [Break](#Break)
- [RightPadding](#RightPadding)
- [LeftPadding](#LeftPadding)
- [Frequency](#Frequency)
<div STYLE="page-break-after: always;"></div>
@@ -318,12 +331,12 @@ func main() {
### <span id="Concat">Concat</span>
<p>合并多个slices到slice中</p>
<p>创建一个新的切片,将传入的切片拼接起来返回。</p>
<b>函数签名:</b>
```go
func Concat[T any](slice []T, slices ...[]T) []T
func Concat[T any](slices ...[]T) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
@@ -525,7 +538,7 @@ func main() {
func DeleteAt[T any](slice []T, index int) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/pJ-d6MUWcvK)</span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/800B1dPBYyd)</span></b>
```go
import (
@@ -565,7 +578,7 @@ func main() {
func DeleteRange[T any](slice []T, start, end int) []T
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/945HwiNrnle)</span></b>
```go
import (
@@ -892,10 +905,46 @@ func main() {
}
```
### <span id="Find">Find (废弃:使用 FindBy)</span>
### <span id="FilterConcurrent">FilterConcurrent</span>
<p>对slice并发执行filter操作。</p>
<b>函数签名:</b>
```go
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
result := slice.FilterConcurrent(nums, isEven, 2)
fmt.Println(result)
// Output:
// [2 4]
}
```
### <span id="Find">Find</span>
<p>遍历slice的元素返回第一个通过predicate函数真值测试的元素</p>
> ⚠️ 本函数已弃用,使用`FindBy`代替。
<b>函数签名:</b>
```go
@@ -964,10 +1013,12 @@ func main() {
}
```
### <span id="FindLast">FindLast(废弃:使用 FindLastBy)</span>
### <span id="FindLast">FindLast</span>
<p>遍历slice的元素返回最后一个通过predicate函数真值测试的元素。</p>
> ⚠️ 本函数已弃用,使用`FindLastBy`代替。
<b>函数签名:</b>
```go
@@ -1131,6 +1182,43 @@ func main() {
}
```
### <span id="ForEachConcurrent">ForEachConcurrent</span>
<p>对slice并发执行foreach操作。</p>
<b>函数签名:</b>
```go
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
result := make([]int, len(nums))
addOne := func(index int, value int) {
result[index] = value + 1
}
slice.ForEachConcurrent(nums, addOne, 4)
fmt.Println(result)
// Output:
// [2 3 4 5 6 7 8 9]
}
```
### <span id="ForEachWithBreak">ForEachWithBreak</span>
<p>遍历切片的元素并为每个元素调用iteratee函数当iteratee函数返回false时终止遍历。</p>
@@ -1239,10 +1327,12 @@ func main() {
}
```
### <span id="IntSlice">IntSlice (已弃用: 使用 go1.18+泛型代替)</span>
### <span id="IntSlice">IntSlice</span>
<p>将接口切片转换为int切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -1268,10 +1358,12 @@ func main() {
}
```
### <span id="InterfaceSlice">InterfaceSlice(已弃用: 使用 go1.18+泛型代替)</span>
### <span id="InterfaceSlice">InterfaceSlice</span>
<p>将值转换为接口切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -1436,7 +1528,7 @@ func main() {
### <span id="Map">Map</span>
<p>对slice中的每个元素执行map函数以创建一个新切片</p>
<p>对slice中的每个元素执行map函数以创建一个新切片</p>
<b>函数签名:</b>
@@ -1468,6 +1560,38 @@ func main() {
}
```
### <span id="MapConcurrent">MapConcurrent</span>
<p>对slice并发执行map操作。</p>
<b>函数签名:</b>
```go
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6}
result := slice.MapConcurrent(nums, func(_, n int) int {
return n * n
}, 4)
fmt.Println(result)
// Output:
// [1 4 9 16 25 36]
}
```
### <span id="FilterMap">FilterMap</span>
<p>返回一个将filter和map操作应用于给定切片的切片。 iteratee回调函数应该返回两个值1结果值。2结果值是否应该被包含在返回的切片中。</p>
@@ -1542,6 +1666,8 @@ func main() {
<p>合并多个切片(不会消除重复元素).</p>
> ⚠️ 本函数已弃用,使用`Concat`代替。
<b>函数签名:</b>
```go
@@ -1601,7 +1727,9 @@ func main() {
### <span id="Reduce">Reduce</span>
<p>将切片中的元素依次运行iteratee函数返回运行结果(废弃建议使用ReduceBy)</p>
<p>将切片中的元素依次运行iteratee函数返回运行结果</p>
> ⚠️ 本函数已弃用,使用`ReduceBy`代替。
<b>函数签名:</b>
@@ -1633,6 +1761,38 @@ func main() {
}
```
### <span id="ReduceConcurrent">ReduceConcurrent</span>
<p>对切片元素执行并发reduce操作。</p>
<b>函数签名:</b>
```go
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;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := slice.ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 1)
fmt.Println(result)
// Output:
// 55
}
```
### <span id="ReduceBy">ReduceBy</span>
<p>对切片元素执行reduce操作。</p>
@@ -2127,10 +2287,12 @@ func main() {
}
```
### <span id="StringSlice">StringSlice(已弃用: 使用 go1.18+泛型代替)</span>
### <span id="StringSlice">StringSlice</span>
<p>将接口切片转换为字符串切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -2279,12 +2441,12 @@ func main() {
### <span id="UniqueBy">UniqueBy</span>
<p>对切片的每个元素调用iteratee函数然后删除重复元素</p>
<p>根据迭代函数返回的值,从输入切片中移除重复元素。此函数保持元素的顺序。</p>
<b>函数签名:</b>
```go
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/UR323iZLDpv)</span></b>
@@ -2304,7 +2466,114 @@ func main() {
fmt.Println(result)
// Output:
// [1 2 0]
// [1 2 3]
}
```
### <span id="UniqueByComparator">UniqueByComparator</span>
<p>使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。</p>
<b>函数签名:</b>
```go
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
uniqueNums := slice.UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
return item == other
})
caseInsensitiveStrings := slice.UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool {
return strings.ToLower(item) == strings.ToLower(other)
})
fmt.Println(uniqueNums)
fmt.Println(caseInsensitiveStrings)
// Output:
// [1 2 3 4 5 6]
// [apple banana cherry date]
}
```
### <span id="UniqueByConcurrent">UniqueByConcurrent</span>
<p>并发的从输入切片中移除重复元素,结果保持元素的顺序。</p>
<b>函数签名:</b>
```go
func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool { return item == other }
result := slice.UniqueByConcurrent(nums, comparator, 4)
fmt.Println(result)
// Output:
// [1 2 3 4 5 6 7]
}
```
### <span id="UniqueByField">UniqueByField</span>
<p>根据struct字段对struct切片去重复。</p>
<b>函数签名:</b>
```go
func UniqueByField[T any](slice []T, field string) ([]T, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/6cifcZSPIGu)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
users := []User{
{ID: 1, Name: "a"},
{ID: 2, Name: "b"},
{ID: 1, Name: "c"},
}
result, err := slice.UniqueByField(users, "ID")
if err != nil {
}
fmt.Println(result)
// Output:
// [{1 a} {2 b}]
}
```
@@ -2568,4 +2837,151 @@ func main() {
// Output:
// okk
}
```
### <span id="SetToDefaultIf">SetToDefaultIf</span>
<p>根据给定给定的predicate判定函数来修改切片中的元素。对于满足的元素将其替换为指定的默认值同时保持元素在切片中的位置不变。函数返回修改后的切片以及被修改的元素个数。</p>
<b>函数签名:</b>
```go
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9AXGlPRC0-A)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "a", "c", "d", "a"}
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
fmt.Println(modifiedStrs)
fmt.Println(count)
// Output:
// [ b c d ]
// 3
}
```
### <span id="Break">Break</span>
<p>根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。</p>
<b>函数签名:</b>
```go
func Break[T any](values []T, predicate func(T) bool) ([]T, []T)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/yLYcBTyeQIz)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
even := func(n int) bool { return n%2 == 0 }
resultEven, resultAfterFirstEven := slice.Break(nums, even)
fmt.Println(resultEven)
fmt.Println(resultAfterFirstEven)
// Output:
// [1]
// [2 3 4 5]
}
```
### <span id="RightPadding">RightPadding</span>
<p>在切片的右部添加元素。</p>
<b>函数签名:</b>
```go
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/0_2rlLEMBXL)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
}
```
### <span id="LeftPadding">LeftPadding</span>
<p>在切片的左部添加元素。</p>
<b>函数签名:</b>
```go
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jlQVoelLl2k)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
```
### <span id="Frequency">Frequency</span>
<p>计算切片中每个元素出现的频率。</p>
<b>函数签名:</b>
```go
func Frequency[T comparable](slice []T) map[T]int
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "b", "c", "c", "c"}
result := slice.Frequency(strs)
fmt.Println(result)
// Output:
// map[a:1 b:2 c:3]
}
```

View File

@@ -60,6 +60,15 @@ import (
- [ContainsAll](#ContainsAll)
- [ContainsAny](#ContainsAny)
- [RemoveWhiteSpace](#RemoveWhiteSpace)
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
- [Ellipsis](#Ellipsis)
- [Shuffle](#Shuffle)
- [Rotate](#Rotate)
- [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups)
<div STYLE="page-break-after: always;"></div>
@@ -1096,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)
@@ -1462,3 +1471,261 @@ func main() {
// hello world
}
```
### <span id="SubInBetween">SubInBetween</span>
<p>获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。</p>
<b>函数签名:</b>
```go
func SubInBetween(str string, start string, end string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/EDbaRvjeNsv)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
str := "abcde"
result1 := strutil.SubInBetween(str, "", "de")
result2 := strutil.SubInBetween(str, "a", "d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// abc
// bc
}
```
### <span id="HammingDistance">HammingDistance</span>
<p>计算两个字符串之间的汉明距离。汉明距离是指对应符号不同的位置数。</p>
<b>函数签名:</b>
```go
func HammingDistance(a, b string) (int, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/glNdQEA9HUi)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1, _ := strutil.HammingDistance("de", "de")
result2, _ := strutil.HammingDistance("a", "d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 0
// 1
}
```
### <span id="Concat">Concat</span>
<p>拼接字符串。length是拼接后字符串的长度如果不确定则传0或负数。</p>
<b>函数签名:</b>
```go
func Concat(length int, 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() {
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}
```
### <span id="Ellipsis">Ellipsis</span>
<p>将字符串截断到指定长度,并在末尾添加省略号。</p>
<b>函数签名:</b>
```go
func Ellipsis(str string, length int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Ellipsis("hello world", 5)
result2 := strutil.Ellipsis("你好,世界!", 2)
result3 := strutil.Ellipsis("😀😃😄😁😆", 3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// hello...
// 你好...
// 😀😃😄...
}
```
### <span id="Shuffle">Shuffle</span>
<p>打乱给定字符串中的字符顺序。</p>
<b>函数签名:</b>
```go
func Shuffle(str string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result := strutil.Shuffle("hello")
fmt.Println(result) //olelh (random order)
}
```
### <span id="Rotate">Rotate</span>
<p>按指定的字符数旋转字符串。</p>
<b>函数签名:</b>
```go
func Rotate(str string, shift int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := Rotate("Hello", 0)
result2 := Rotate("Hello", 1)
result3 := Rotate("Hello", 2)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello
// oHell
// loHel
}
```
### <span id="TemplateReplace">TemplateReplace</span>
<p>将模板字符串中的占位符替换为map中的相应值。占位符括在花括号中例如 {key}。例如模板字符串为“Hello, {name}!”map为{"name": "world"}结果将为“Hello, world!”。</p>
<b>函数签名:</b>
```go
func TemplateReplace(template string, data map[string]string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
result := strutil.TemplateReplace(template, data)
fmt.Println(result)
// Output:
// Hello, my name is Bob, I'm 20 years old.
}
```
### <span id="RegexMatchAllGroups">RegexMatchAllGroups</span>
<p>使用正则表达式匹配字符串中的所有子组并返回结果。</p>
<b>函数签名:</b>
```go
func RegexMatchAllGroups(pattern, str string) [][]string
```
<b>示例:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
str := "Emails: john.doe@example.com and jane.doe@example.com"
result := strutil.RegexMatchAllGroups(pattern, str)
fmt.Println(result[0])
fmt.Println(result[1])
// Output:
// [john.doe@example.com john.doe example com]
// [jane.doe@example.com jane.doe example com]
}
```

View File

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

View File

@@ -43,6 +43,10 @@ import (
- [ToInterface](#ToInterface)
- [Utf8ToGbk](#Utf8ToGbk)
- [GbkToUtf8](#GbkToUtf8)
- [ToStdBase64](#ToStdBase64)
- [ToUrlBase64](#ToUrlBase64)
- [ToRawStdBase64](#ToRawStdBase64)
- [ToRawUrlBase64](#ToRawUrlBase64)
<div STYLE="page-break-after: always;"></div>
@@ -567,6 +571,7 @@ func main() {
}
```
### <span id="EncodeByte">EncodeByte</span>
<p>Encode data to byte slice.</p>
@@ -632,69 +637,6 @@ func main() {
}
```
### <span id="DeepClone">DeepClone</span>
<p>Creates a deep copy of passed item, can't clone unexported field of struct.</p>
<b>Signature:</b>
```go
func DeepClone[T any](src T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/j4DP5dquxnk)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
type Struct struct {
Str string
Int int
Float float64
Bool bool
Nil interface{}
unexported string
}
cases := []interface{}{
true,
1,
0.1,
map[string]int{
"a": 1,
"b": 2,
},
&Struct{
Str: "test",
Int: 1,
Float: 0.1,
Bool: true,
Nil: nil,
// unexported: "can't be cloned",
},
}
for _, item := range cases {
cloned := convertor.DeepClone(item)
isPointerEqual := &cloned == &item
fmt.Println(cloned, isPointerEqual)
}
// Output:
// true false
// 1 false
// 0.1 false
// map[a:1 b:2] false
// &{test 1 0.1 true <nil> } false
}
```
### <span id="CopyProperties">CopyProperties</span>
@@ -775,41 +717,6 @@ func main() {
}
```
### <span id="ToInterface">ToInterface</span>
<p>Converts reflect value to its interface type.</p>
<b>Signature:</b>
```go
func ToInterface(v reflect.Value) (value interface{}, ok bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/syqw0-WG7Xd)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
val := reflect.ValueOf("abc")
iVal, ok := convertor.ToInterface(val)
fmt.Printf("%T\n", iVal)
fmt.Printf("%v\n", iVal)
fmt.Println(ok)
// Output:
// string
// abc
// true
}
```
### <span id="Utf8ToGbk">Utf8ToGbk</span>
<p>Converts utf8 encoding data to GBK encoding data.</p>
@@ -875,4 +782,338 @@ func main() {
// true
// hello
}
```
### <span id="ToStdBase64">ToStdBase64</span>
<p>Convert a value to a string encoded in standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
<b>Signature:</b>
```go
func ToStdBase64(value any) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/_fLJqJD3NMo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
afterEncode := convertor.ToStdBase64(nil)
fmt.Println(afterEncode)
afterEncode = convertor.ToStdBase64("")
fmt.Println(afterEncode)
stringVal := "hello"
afterEncode = convertor.ToStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToStdBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToStdBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToStdBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
//
//
// aGVsbG8=
// aGVsbG8=
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
// MTIzLjQ1Ng==
// dHJ1ZQ==
// ZXJy
}
```
### <span id="ToUrlBase64">ToUrlBase64</span>
<p>Convert a value to a string encoded in url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
<b>Signature:</b>
```go
func ToUrlBase64(value any) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/C_d0GlvEeUR)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
afterEncode := convertor.ToUrlBase64(nil)
fmt.Println(afterEncode)
stringVal := "hello"
afterEncode = convertor.ToUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToUrlBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToUrlBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToUrlBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToUrlBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
//
// aGVsbG8=
// aGVsbG8=
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
// MTIzLjQ1Ng==
// dHJ1ZQ==
// ZXJy
}
```
### <span id="ToRawStdBase64">ToRawStdBase64</span>
<p>Convert a value to a string encoded in raw standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
<b>Signature:</b>
```go
func ToRawStdBase64(value any) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wSAr3sfkDcv)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
stringVal := "hello"
afterEncode := convertor.ToRawStdBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToRawStdBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToRawStdBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToRawStdBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToRawStdBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawStdBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
```
### <span id="ToRawUrlBase64">ToRawUrlBase64</span>
<p> Convert a value to a string encoded in raw url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
<b>Signature:</b>
```go
func ToRawUrlBase64(value any) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/HwdDPFcza1O)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
stringVal := "hello"
afterEncode := convertor.ToRawUrlBase64(stringVal)
fmt.Println(afterEncode)
byteSliceVal := []byte("hello")
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
fmt.Println(afterEncode)
intVal := 123
afterEncode = convertor.ToRawUrlBase64(intVal)
fmt.Println(afterEncode)
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
A string
B int
}{"hello", 3}}
afterEncode = convertor.ToRawUrlBase64(mapVal)
fmt.Println(afterEncode)
floatVal := 123.456
afterEncode = convertor.ToRawUrlBase64(floatVal)
fmt.Println(afterEncode)
boolVal := true
afterEncode = convertor.ToRawUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
```
### <span id="DeepClone">DeepClone</span>
<p>Creates a deep copy of passed item, can't clone unexported field of struct.</p>
<b>Signature:</b>
```go
func DeepClone[T any](src T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/j4DP5dquxnk)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
type Struct struct {
Str string
Int int
Float float64
Bool bool
Nil interface{}
unexported string
}
cases := []interface{}{
true,
1,
0.1,
map[string]int{
"a": 1,
"b": 2,
},
&Struct{
Str: "test",
Int: 1,
Float: 0.1,
Bool: true,
Nil: nil,
},
}
for _, item := range cases {
cloned := convertor.DeepClone(item)
isPointerEqual := &cloned == &item
fmt.Println(cloned, isPointerEqual)
}
// Output:
// true false
// 1 false
// 0.1 false
// map[a:1 b:2] false
// &{test 1 0.1 true <nil> } false
}
```

View File

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

View File

@@ -26,6 +26,8 @@ import (
- [Remove](#Remove)
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [IndexOfFunc](#IndexOfFunc)
- [LastIndexOfFunc](#LastIndexOfFunc)
- [IsEmpty](#IsEmpty)
- [Contain](#Contain)
- [ValueOf](#ValueOf)
@@ -197,6 +199,59 @@ func main() {
```
### <span id="IndexOfFunc">IndexOfFunc</span>
<p> IndexOfFunc returns the first index satisfying the functional predicate f(v) bool. if not found return -1.</p>
<b>Signature:</b>
```go
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1, 2, 3})
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 1 })) //0
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 0 })) //-1
}
```
### <span id="LastIndexOfFunc">LastIndexOfFunc</span>
<p>LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying the functional predicate f(T) bool. if not found return -1.</p>
<b>Signature:</b>
```go
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1, 2, 3, 1})
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1
}
```
### IsEmpty
Returns true if this list does not contain any elements.

View File

@@ -32,6 +32,7 @@ import (
- [Iterate](#Iterate)
- [Keys](#Keys)
- [Values](#Values)
- [FilterByValue](#FilterByValue)
<div STYLE="page-break-after: always;"></div>
@@ -311,4 +312,77 @@ func main() {
}
```
### <span id="FilterByValue">FilterByValue</span>
<p>Returns a filtered HashMap.</p>
<b>Signature:</b>
```go
func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap
```
<b>Example:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := hashmap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
hm.Put("d", 4)
hm.Put("e", 5)
hm.Put("f", 6)
filteredHM := hm.FilterByValue(func(value any) bool {
return value.(int) == 1 || value.(int) == 3
})
fmt.Println(filteredHM.Size()) //2
}
```
### <span id="ToInterface">ToInterface</span>
<p>Converts reflect value to its interface type.</p>
<b>Signature:</b>
```go
func ToInterface(v reflect.Value) (value interface{}, ok bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/syqw0-WG7Xd)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
val := reflect.ValueOf("abc")
iVal, ok := convertor.ToInterface(val)
fmt.Printf("%T\n", iVal)
fmt.Printf("%v\n", iVal)
fmt.Println(ok)
// Output:
// string
// abc
// true
}
```

View File

@@ -0,0 +1,416 @@
# Optional
Optional is a type that may or may not contain a non-nil value.
<div STYLE="page-break-after: always;"></div>
## Source
- [https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go](https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go)
<div STYLE="page-break-after: always;"></div>
## Usage
```go
import (
"github.com/duke-git/lancet/v2/datastructure/optional"
)
```
<div STYLE="page-break-after: always;"></div>
## Index
- [Of](#Of)
- [FromNillable](#FromNillable)
- [Default](#Default)
- [IsNotNil](#IsNotNil)
- [IsNil](#IsNil)
- [IsNotNil](#IsNotNil)
- [IfNotNilOrElse](#IfNotNilOrElse)
- [Umwarp](#Umwarp)
- [OrElse](#OrElse)
- [OrElseGet](#OrElseGet)
- [OrElseTrigger](#OrElseTrigger)
<div STYLE="page-break-after: always;"></div>
## Documentation
### <span id="Of">Of</span>
<p>Returns an Optional with a non-nil value.</p>
<b>Signature:</b>
```go
func Of[T any](value T) Optional[T]
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
value := 42
opt := optional.Of(value)
fmt.Println(opt.Get())
// Output:
// 42
}
```
### <span id="FromNillable">FromNillable</span>
<p>Returns an Optional for a given value, which may be nil.</p>
<b>Signature:</b>
```go
func FromNillable[T any](value *T) Optional[T]
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
var value *int = nil
opt := optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
value = new(int)
*value = 42
opt = optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
// Output:
// false
// true
}
```
### <span id="Default">Default</span>
<p>Returns an default Optional instance.</p>
<b>Signature:</b>
```go
func Default[T any]() Optional[T]
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
fmt.Println(optDefault.IsNil())
// Output:
// true
}
```
### <span id="IsNil">IsNil</span>
<p>Checks if the Optional is nil.</p>
<b>Signature:</b>
```go
func (o Optional[T]) IsNil() bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
fmt.Println(optDefault.IsNil())
// Output:
// true
}
```
### <span id="IsNotNil">IsNotNil</span>
<p>Checks if there is a value not nil.</p>
<b>Signature:</b>
```go
func (o Optional[T]) IsNotNil() bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
var value *int = nil
opt := optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
value = new(int)
*value = 42
opt = optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
// Output:
// false
// true
}
```
### <span id="IfNotNil">IfNotNil</span>
<p>Performs the given action with the value if a value is present.</p>
<b>Signature:</b>
```go
func (o Optional[T]) IfNotNil(action func(value T))
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
called := false
action := func(value int) { called = true }
optDefault := optional.Default[int]()
optDefault.IfNotNil(action)
fmt.Println(called)
called = false // Reset for next test
optWithValue := optional.Of(42)
optWithValue.IfNotNil(action)
fmt.Println(optWithValue.IsNotNil())
// Output:
// false
// true
}
```
### <span id="IfNotNilOrElse">IfNotNilOrElse</span>
<p>Performs the action with the value if not nil, otherwise performs the fallback action.</p>
<b>Signature:</b>
```go
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func())
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
calledWithValue := false
valueAction := func(value int) { calledWithValue = true }
emptyAction := func() { t.Errorf("Empty action should not be called when value is present") }
optWithValue := optional.Of(42)
optWithValue.IfNotNilOrElse(valueAction, emptyAction)
fmt.Println(calledWithValue)
calledWithEmpty := false
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
emptyAction = func() { calledWithEmpty = true }
optDefault := optional.Default[int]()
optDefault.IfNotNilOrElse(valueAction, emptyAction)
fmt.Println(calledWithEmpty)
// Output:
// true
// true
}
```
### <span id="Unwrap">Unwrap</span>
<p>Returns the value if not nil, otherwise panics.</p>
<b>Signature:</b>
```go
func (o Optional[T]) Unwrap() T
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
value := 42
opt := optional.Of(value)
fmt.Println(opt.Unwrap())
// Output:
// 42
}
```
### <span id="OrElse">OrElse</span>
<p>Returns the value if not nill, otherwise returns other.</p>
<b>Signature:</b>
```go
func (o Optional[T]) OrElse(other T) T
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
val := optDefault.OrElse(100)
fmt.Println(val)
optWithValue := optional.Of(42)
val = optWithValue.OrElse(100)
fmt.Println(val)
// Output:
// 100
// 42
}
```
### <span id="OrElseGet">OrElseGet</span>
<p>Returns the value if not nil, otherwise invokes action and returns the result.</p>
<b>Signature:</b>
```go
func (o Optional[T]) OrElseGet(action func() T) T
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
action := func() int { return 100 }
val := optDefault.OrElseGet(action)
fmt.Println(val)
// Output:
// 100
}
```
### <span id="OrElseTrigger">OrElseTrigger</span>
<p>Returns the value if present, otherwise returns an error.</p>
<b>Signature:</b>
```go
OrElseTrigger(errorHandler func() error) (T, error)
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
fmt.Println(err.Error())
optWithValue := optional.Of(42)
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
fmt.Println(val)
fmt.Println(err)
// Output:
// no value
// 42
// nil
}
```

View File

@@ -22,9 +22,9 @@ import (
## Index
- [NewSet](#NewSet)
- [NewSetFromSlice](#NewSetFromSlice)
- [Values](#Values)
- [New](#New)
- [FromSlice](#FromSlice)
- [Values<sup>deprecated</sup>](#Values)
- [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
- [AddIfNotExistBy](#AddIfNotExistBy)
@@ -41,20 +41,23 @@ import (
- [Intersection](#Intersection)
- [SymmetricDifference](#SymmetricDifference)
- [Minus](#Minus)
- [Pop](#Pop)
- [ToSlice](#ToSlice)
- [ToSortedSlice](#ToSortedSlice)
<div STYLE="page-break-after: always;"></div>
## Documentation
### <span id="NewSet">NewSet</span>
### <span id="New">New</span>
<p>Create a set instance</p>
<b>Signature:</b>
```go
type Set[T comparable] map[T]bool
func NewSet[T comparable](items ...T) Set[T]
type Set[T comparable] map[T]struct{}
func New[T comparable](items ...T) Set[T]
```
<b>Example:</b>
@@ -68,19 +71,19 @@ import (
)
func main() {
st := set.NewSet[int](1,2,2,3)
st := set.New[int](1,2,2,3)
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="NewSetFromSlice">NewSetFromSlice</span>
### <span id="FromSlice">FromSlice</span>
<p>Create a set from slice</p>
<b>Signature:</b>
```go
func NewSetFromSlice[T comparable](items []T) Set[T]
func FromSlice[T comparable](items []T) Set[T]
```
<b>Example:</b>
@@ -94,14 +97,16 @@ import (
)
func main() {
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
st := set.FromSlice([]int{1, 2, 2, 3})
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="Values">Values</span>
<p>Return slice of all set data</p>
<p>Return slice of all set data.</p>
> ⚠️ This function is deprecated. use `ToSlice` instead.
<b>Signature:</b>
@@ -120,7 +125,7 @@ import (
)
func main() {
st := set.NewSet[int](1,2,2,3)
st := set.New[int](1,2,2,3)
fmt.Println(st.Values()) //1,2,3
}
```
@@ -146,7 +151,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
fmt.Println(st.Values()) //1,2,3
@@ -174,7 +179,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
r1 := st.AddIfNotExist(1)
@@ -207,7 +212,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2)
ok := st.AddIfNotExistBy(3, func(val int) bool {
@@ -246,7 +251,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
set.Delete(3)
@@ -275,7 +280,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
fmt.Println(st.Contain(1)) //true
@@ -304,9 +309,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(1, 2)
set3 := set.NewSet(1, 2, 3, 4)
set1 := set.New(1, 2, 3)
set2 := set.New(1, 2)
set3 := set.New(1, 2, 3, 4)
fmt.Println(set1.ContainAll(set2)) //true
fmt.Println(set1.ContainAll(set3)) //false
@@ -334,7 +339,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
fmt.Println(set1.Size()) //3
}
@@ -361,7 +366,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
set2 := set1.Clone()
fmt.Println(set1.Size() == set2.Size()) //true
@@ -390,9 +395,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(1, 2, 3)
set3 := set.NewSet(1, 2, 3, 4)
set1 := set.New(1, 2, 3)
set2 := set.New(1, 2, 3)
set3 := set.New(1, 2, 3, 4)
fmt.Println(set1.Equal(set2)) //true
fmt.Println(set1.Equal(set3)) //false
@@ -420,7 +425,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
arr := []int{}
set.Iterate(func(item int) {
arr = append(arr, item)
@@ -451,7 +456,7 @@ import (
)
func main() {
s := set.NewSet(1, 2, 3, 4, 5)
s := set.New(1, 2, 3, 4, 5)
var sum int
@@ -488,8 +493,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet()
set1 := set.New(1, 2, 3)
set2 := set.New()
fmt.Println(set1.IsEmpty()) //false
fmt.Println(set2.IsEmpty()) //true
@@ -517,8 +522,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.Union(set2)
fmt.Println(set3.Values()) //1,2,3,4,5
@@ -546,8 +551,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.Intersection(set2)
fmt.Println(set3.Values()) //2,3
@@ -575,8 +580,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.SymmetricDifference(set2)
fmt.Println(set3.Values()) //1,4,5
@@ -604,9 +609,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set3 := set.NewSet(2, 3)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set.New(2, 3)
res1 := set1.Minus(set2)
fmt.Println(res1.Values()) //1
@@ -637,7 +642,7 @@ import (
)
func main() {
s := set.NewSet[int]()
s := set.New[int]()
s.Add(1)
s.Add(2)
s.Add(3)
@@ -648,3 +653,58 @@ func main() {
fmt.Println(ok) // true
}
```
### <span id="ToSlice">ToSlice</span>
<p>returns a slice containing all values of the set.</p>
<b>Signature:</b>
```go
func (s Set[T]) ToSlice() (v T, ok bool)
```
<b>Example:</b>
```go
func main() {
s := set.New(1, 2, 3, 4, 5)
val := s.ToSlice()
fmt.Println(val) // [2 3 4 5 1]
}
```
### <span id="ToSortedSlice">ToSortedSlice</span>
<p>returns a sorted slice containing all values of the set</p>
<b>Signature:</b>
```go
func (s Set[T]) ToSortedSlice() (v T, ok bool)
```
<b>Example:</b>
```go
func main() {
s1 := set.New(1, 2, 3, 4, 5)
type Person struct {
Name string
Age int
}
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
return v1 < v2
})
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
return v1.Age < v2.Age
})
fmt.Println(res1) // [1 2 3 4 5]
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
}
```

View File

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

View File

@@ -26,6 +26,7 @@ import (
- [CreateFile](#CreateFile)
- [CreateDir](#CreateDir)
- [CopyFile](#CopyFile)
- [CopyDir](#CopyDir)
- [CurrentPath](#CurrentPath)
- [FileMode](#FileMode)
- [MiMeType](#MiMeType)
@@ -45,11 +46,12 @@ import (
- [Sha](#Sha)
- [ReadCsvFile](#ReadCsvFile)
- [WriteCsvFile](#WriteCsvFile)
- [WriteCsvFile](#WriteCsvFile)
- [WriteMapsToCsv](#WriteMapsToCsv)
- [WriteStringToFile](#WriteStringToFile)
- [WriteBytesToFile](#WriteBytesToFile)
- [ReadFile](#ReadFile)
- [ChunkRead](#ChunkRead)
- [ParallelChunkRead](#ParallelChunkRead)
<div STYLE="page-break-after: always;"></div>
@@ -163,6 +165,34 @@ func main() {
}
```
### <span id="CopyDir">CopyDir</span>
<p>Copy src directory to dst directory, it will copy all files and directories recursively. the access permission will be the same as the source directory. if dstPath exists, it will return an error.</p>
<b>Signature:</b>
```go
func CopyDir(srcPath string, dstPath string) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/YAyFTA_UuPb)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.CopyFile("./test_src", "./test_dest")
if err != nil {
fmt.Println(err)
}
}
```
### <span id="CurrentPath">CurrentPath</span>
<p>return current absolute path.</p>
@@ -760,7 +790,7 @@ func main() {
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/umAIomZFV1c)</span></b>
```go
package main
@@ -933,3 +963,115 @@ func main() {
// Disallow: /deny
}
```
### <span id="ChunkRead">ChunkRead</span>
<p>reads a block from the file at the specified offset and returns all lines within the block.</p>
<b>Signature :</b>
```go
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/r0hPmKWhsgf)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100
// test1.csv file content:
// Lili,22,female
// Jim,21,male
filePath := "./testdata/test1.csv"
f, err := os.Open(filePath)
if err != nil {
return
}
defer f.Close()
var bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultChunkSizeMB*mb)
},
}
lines, err := fileutil.ChunkRead(f, 0, 100, &bufPool)
if err != nil {
return
}
fmt.Println(lines[0])
fmt.Println(lines[1])
// Output:
// Lili,22,female
// Jim,21,male
}
```
### <span id="ParallelChunkRead">ParallelChunkRead</span>
<p>Reads the file in parallel and send each chunk of lines to the specified channel.</p>
<b>Signature :</b>
```go
// filePath: file path.
// chunkSizeMB: The size of the block (in MB, the default is 100MB when set to 0). Setting it too large will be detrimental. Adjust it as appropriate.
// maxGoroutine: The number of concurrent read chunks, the number of CPU cores used when set to 0.
// linesCh: The channel used to receive the returned results.
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/teMXnCsdSEw)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
// test1.csv file content:
// Lili,22,female
// Jim,21,male
filePath := "./testdata/test1.csv"
go fileutil.ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
var totalLines int
for lines := range linesCh {
totalLines += len(lines)
for _, line := range lines {
fmt.Println(line)
}
}
fmt.Println(totalLines)
// Output:
// Lili,22,female
// Jim,21,male
// 2
}
```

View File

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

View File

@@ -7,6 +7,7 @@ Package function can control the flow of function execution and support part of
## Source:
- [https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go)
- [https://github.com/duke-git/lancet/blob/main/function/predicate.go](https://github.com/duke-git/lancet/blob/main/function/predicate.go)
- [https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go)
<div STYLE="page-break-after: always;"></div>
@@ -27,11 +28,21 @@ import (
- [Before](#Before)
- [CurryFn](#CurryFn)
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Debounce](#Debounce)
- [Debounced<sup>deprecated</sup>](#Debounced)
- [Delay](#Delay)
- [Schedule](#Schedule)
- [Pipeline](#Pipeline)
- [Watcher](#Watcher)
- [And](#And)
- [Or](#Or)
- [Negate](#Negate)
- [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand)
- [AcceptIf](#AcceptIf)
- [Throttle](#Throttle)
<div STYLE="page-break-after: always;"></div>
@@ -183,11 +194,59 @@ func main() {
// ABCDE
}
```
### <span id="Debounce">Debounce</span>
<p>Creates a debounced version of the provided function. The debounced function will only invoke the original function after the specified delay has passed since the last time it was invoked. It also supports canceling the debounce.</p>
<b>Signature:</b>
```go
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn 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/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
```
### <span id="Debounced">Debounced</span>
<p>Creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.</p>
> ⚠️ This function is deprecated. use `Debounce` instead.
<b>Signature:</b>
```go
@@ -403,3 +462,324 @@ func longRunningTask() {
}
```
### <span id="And">And</span>
<p>Returns a composed predicate that represents the logical AND of a list of predicates. It evaluates to true only if all predicates evaluate to true for the given value.</p>
<b>Signature:</b>
```go
func And[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/dTBHJMQ0zD2)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isNumericAndLength5 := function.And(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcde"))
// Output:
// true
// false
// false
}
```
### <span id="Or">Or</span>
<p>Returns a composed predicate that represents the logical OR of a list of predicates. It evaluates to true if at least one of the predicates evaluates to true for the given value.</p>
<b>Signature:</b>
```go
func Or[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/LitCIsDFNDA)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
containsDigitOrSpecialChar := function.Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
)
fmt.Println(containsDigitOrSpecialChar("hello!"))
fmt.Println(containsDigitOrSpecialChar("hello"))
// Output:
// true
// false
}
```
### <span id="Negate">Negate</span>
<p>Returns a predicate that represents the logical negation of this predicate.</p>
<b>Signature:</b>
```go
func Negate[T any](predicate func(T) bool) func(T) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jbI8BtgFnVE)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
// Define some simple predicates for demonstration
isUpperCase := func(s string) bool {
return strings.ToUpper(s) == s
}
isLowerCase := func(s string) bool {
return strings.ToLower(s) == s
}
isMixedCase := function.Negate(function.Or(isUpperCase, isLowerCase))
fmt.Println(isMixedCase("ABC"))
fmt.Println(isMixedCase("AbC"))
// Output:
// false
// true
}
```
### <span id="Nor">Nor</span>
<p>Returns a composed predicate that represents the logical NOR of a list of predicates. It evaluates to true only if all predicates evaluate to false for the given value.</p>
<b>Signature:</b>
```go
func Nor[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/2KdCoBEOq84)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
match := function.Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("dbcdckkeee"))
match = function.Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("0123456789"))
// Output:
// true
// false
}
```
### <span id="Nand">Nand</span>
<p>Returns a composed predicate that represents the logical NAND of a list of predicates. It evaluates to true only if all predicates evaluate to false for the given value.</p>
<b>Signature:</b>
```go
func Nand[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Rb-FdNGpgSO)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isNumericAndLength5 := function.Nand(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcdef"))
// Output:
// false
// false
// true
}
```
### <span id="Xnor">Xnor</span>
<p>Returns a composed predicate that represents the logical XNOR of a list of predicates. It evaluates to true only if all predicates evaluate to true or false for the given value.</p>
<b>Signature:</b>
```go
func Xnor[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/FJxko8SFbqc)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isEven := func(i int) bool { return i%2 == 0 }
isPositive := func(i int) bool { return i > 0 }
match := function.Xnor(isEven, isPositive)
fmt.Println(match(2))
fmt.Println(match(-3))
fmt.Println(match(3))
// Output:
// true
// true
// false
}
```
### <span id="AcceptIf">AcceptIf</span>
<p>AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure. A predicate function that takes an argument of type T and returns a bool. An apply function that also takes an argument of type T and returns a modified value of the same type.</p>
<b>Signature:</b>
```go
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/XlXHHtzCf7d)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
adder := function.AcceptIf(
function.And(
func(x int) bool {
return x > 10
}, func(x int) bool {
return x%2 == 0
}),
func(x int) int {
return x + 1
},
)
result, ok := adder(20)
fmt.Println(result)
fmt.Println(ok)
result, ok = adder(21)
fmt.Println(result)
fmt.Println(ok)
// Output:
// 21
// true
// 0
// false
}
```
### <span id="Throttle">Throttle</span>
<p>Throttle creates a throttled version of the provided function. The returned function guarantees that it will only be invoked at most once per interval.</p>
<b>Signature:</b>
```go
func Throttle(fn func(), interval time.Duration) 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/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
throttledFn := function.Throttle(fn, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
}
```

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,10 @@ import (
- [RoundToFloat](#RoundToFloat)
- [RoundToString](#RoundToString)
- [TruncRound](#TruncRound)
- [CeilToFloat](#CeilToFloat)
- [CeilToString](#CeilToString)
- [FloorToFloat](#FloorToFloat)
- [FloorToString](#FloorToString)
- [Range](#Range)
- [RangeWithStep](#RangeWithStep)
- [AngleToRadian](#AngleToRadian)
@@ -47,6 +51,7 @@ import (
- [Log](#Log)
- [Sum](#Sum)
- [Abs](#Abs)
- [Div](#Div)
<div STYLE="page-break-after: always;"></div>
@@ -392,7 +397,7 @@ func main() {
<b>Signature:</b>
```go
func RoundToFloat(x float64, n int) float64
func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ghyb528JRJL)</span></b>
@@ -428,7 +433,7 @@ func main() {
<b>Signature:</b>
```go
func RoundToString(x float64, n int) string
func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/kZwpBRAcllO)</span></b>
@@ -464,7 +469,7 @@ func main() {
<b>Signature:</b>
```go
func TruncRound(x float64, n int) float64
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/aumarSHIGzP)</span></b>
@@ -493,6 +498,150 @@ func main() {
}
```
### <span id="CeilToFloat">CeilToFloat</span>
<p>Round float up n decimal places.</p>
<b>Signature:</b>
```go
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/8hOeSADZPCo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.CeilToFloat(3.14159, 1)
result2 := mathutil.CeilToFloat(3.14159, 2)
result3 := mathutil.CeilToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5
}
```
### <span id="CeilToString">CeilToString</span>
<p>Round float up n decimal places.</p>
<b>Signature:</b>
```go
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wy5bYEyUKKG)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.CeilToString(3.14159, 1)
result2 := mathutil.CeilToString(3.14159, 2)
result3 := mathutil.CeilToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5.0000
}
```
### <span id="FloorToFloat">FloorToFloat</span>
<p>Round float down n decimal places.</p>
<b>Signature:</b>
```go
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/vbCBrQHZEED)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.FloorToFloat(3.14159, 1)
result2 := mathutil.FloorToFloat(3.14159, 2)
result3 := mathutil.FloorToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5
}
```
### <span id="FloorToString">FloorToString</span>
<p>Round float down n decimal places.</p>
<b>Signature:</b>
```go
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Qk9KPd2IdDb)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.FloorToString(3.14159, 1)
result2 := mathutil.FloorToString(3.14159, 2)
result3 := mathutil.FloorToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5.0000
}
```
### <span id="Range">Range</span>
<p>Creates a slice of numbers from start with specified count, element step is 1.</p>
@@ -964,17 +1113,52 @@ import (
)
func main() {
result1 := Abs(-1)
result2 := Abs(-0.1)
result3 := Abs(float32(0.2))
result1 := mathutil.Abs(-1)
result2 := mathutil.Abs(-0.1)
result3 := mathutil.Abs(float32(0.2))
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 1
// 0.1
// 0.2
// Output:
// 1
// 0.1
// 0.2
}
```
### <span id="Div">Div</span>
<p>Returns the result of x divided by y.</p>
<b>Signature:</b>
```go
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/WLxDdGXXYat)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Div(9, 4)
result2 := mathutil.Div(1, 2)
result3 := mathutil.Div(0, 666)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 2.25
// 0.5
// 0
}
```

View File

@@ -624,7 +624,9 @@ func main() {
### <span id="HttpGet">HttpGet</span>
<p>Send http get request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http get request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -666,7 +668,9 @@ func main() {
### <span id="HttpPost">HttpPost</span>
<p>Send http post request.(Deprecated: use SendRequest for replacement)</p>
<p>Send http post request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -713,7 +717,9 @@ func main() {
### <span id="HttpPut">HttpPut</span>
<p>Send http put request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http put request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -763,7 +769,9 @@ func main() {
### <span id="HttpDelete">HttpDelete</span>
<p>Send http delete request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http delete request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -802,7 +810,9 @@ func main() {
### <span id="HttpPatch">HttpPatch</span>
<p>Send http patch request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http patch request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>

View File

@@ -24,8 +24,8 @@ import (
- [Of](#Of)
- [Unwrap](#Unwrap)
- [UnwarpOr](#UnwarpOr)
- [UnwarpOrDefault](#UnwarpOrDefault)
- [UnwrapOr](#UnwrapOr)
- [UnwrapOrDefault](#UnwrapOrDefault)
- [ExtractPointer](#ExtractPointer)
<div STYLE="page-break-after: always;"></div>
@@ -103,13 +103,13 @@ func main() {
```
### <span id="UnwarpOr">UnwarpOr</span>
### <span id="UnwrapOr">UnwrapOr</span>
<p>Returns the value from the pointer or fallback if the pointer is nil.</p>
<b>Signature:</b>
```go
UnwarpOr[T any](p *T, fallback T) T
UnwrapOr[T any](p *T, fallback T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/mmNaLC38W8C)</span></b>
@@ -129,10 +129,10 @@ func main() {
var c *int
var d *string
result1 := pointer.UnwarpOr(&a, 456)
result2 := pointer.UnwarpOr(&b, "abc")
result3 := pointer.UnwarpOr(c, 456)
result4 := pointer.UnwarpOr(d, "def")
result1 := pointer.UnwrapOr(&a, 456)
result2 := pointer.UnwrapOr(&b, "abc")
result3 := pointer.UnwrapOr(c, 456)
result4 := pointer.UnwrapOr(d, "def")
fmt.Println(result1)
fmt.Println(result2)
@@ -148,13 +148,13 @@ func main() {
```
### <span id="UnwarpOrDefault">UnwarpOrDefault</span>
### <span id="UnwrapOrDefault">UnwrapOrDefault</span>
<p>Returns the value from the pointer or the default value if the pointer is nil.</p>
<b>Signature:</b>
```go
UnwarpOrDefault[T any](p *T) T
UnwrapOrDefault[T any](p *T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
@@ -174,10 +174,10 @@ func main() {
var c *int
var d *string
result1 := pointer.UnwarpOrDefault(&a)
result2 := pointer.UnwarpOrDefault(&b)
result3 := pointer.UnwarpOrDefault(c)
result4 := pointer.UnwarpOrDefault(d)
result1 := pointer.UnwrapOrDefault(&a)
result2 := pointer.UnwrapOrDefault(&b)
result3 := pointer.UnwrapOrDefault(c)
result4 := pointer.UnwrapOrDefault(d)
fmt.Println(result1)
fmt.Println(result2)

View File

@@ -25,15 +25,21 @@ import (
- [RandBytes](#RandBytes)
- [RandInt](#RandInt)
- [RandString](#RandString)
- [RandFromGivenSlice](#RandFromGivenSlice)
- [RandSliceFromGivenSlice](#RandSliceFromGivenSlice)
- [RandUpper](#RandUpper)
- [RandLower](#RandLower)
- [RandNumeral](#RandNumeral)
- [RandNumeralOrLetter](#RandNumeralOrLetter)
- [RandSymbolChar](#RandSymbolChar)
- [UUIdV4](#UUIdV4)
- [RandIntSlice](#RandIntSlice)
- [RandUniqueIntSlice](#RandUniqueIntSlice)
- [RandFloat](#RandFloat)
- [RandFloats](#RandFloats)
- [RandStringSlice](#RandStringSlice)
- [RandBool](#RandBool)
- [RandBoolSlice](#RandBoolSlice)
<div STYLE="page-break-after: always;"></div>
@@ -117,6 +123,60 @@ func main() {
}
```
### <span id="RandFromGivenSlice">RandFromGivenSlice</span>
<p>Generate a random element from given slice.</p>
<b>Signature:</b>
```go
func RandFromGivenSlice[T any](slice []T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randomSet := []any{"a", 8, "hello", true, 1.1}
randElm := random.RandFromGivenSlice(randomSet)
fmt.Println(randElm)
}
```
### <span id="RandSliceFromGivenSlice">RandSliceFromGivenSlice</span>
<p>Generate a random slice of length num from given slice.</p>
<b>Signature:</b>
```go
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon", "mango", "nectarine", "orange"}
chosen3goods := random.RandSliceFromGivenSlice(goods, 3, false)
fmt.Println(chosen3goods)
}
```
### <span id="RandUpper">RandUpper</span>
<p>Generate a random upper case string</p>
@@ -276,15 +336,41 @@ func main() {
}
```
### <span id="RandIntSlice">RandIntSlice</span>
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
<p>Generate a slice of random int of length n that do not repeat.</p>
<p>Generate a slice of random int. Number range in [min, max)</p>
<b>Signature:</b>
```go
func RandUniqueIntSlice(n, min, max int) []int
func RandIntSlice(length, min, max int) []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/random"
)
func main() {
result := random.RandIntSlice(5, 0, 10)
fmt.Println(result) //[1 4 7 1 5] (random)
}
```
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
<p>Generate a slice of random int of length that do not repeat. Number range in [min, max)</p>
<b>Signature:</b>
```go
func RandUniqueIntSlice(length, min, max int) []int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uBkRSOz73Ec)</span></b>
@@ -331,12 +417,12 @@ func main() {
### <span id="RandFloats">RandFloats</span>
<p>Generate a slice of random float64 numbers of length n that do not repeat.</p>
<p>Generate a slice of random float64 numbers of length n that do not repeat. Number range in [min, max)</p>
<b>Signature:</b>
```go
func RandFloats(n int, min, max float64, precision int) []float64
func RandFloats(length int, min, max float64, precision int) []float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
@@ -353,4 +439,86 @@ func main() {
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
fmt.Println(floatNumbers) //[3.42 3.99 1.3 2.38 4.23] (random)
}
```
### <span id="RandStringSlice">RandStringSlice</span>
<p>Generate a slice of random string of length strLen based on charset. chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars. or a combination of them.</p>
<b>Signature:</b>
```go
func RandStringSlice(charset string, sliceLen, strLen int) []string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
strs := random.RandStringSlice(random.Letters, 4, 6)
fmt.Println(strs)
// output random string slice like below:
//[CooSMq RUFjDz FAeMPf heRyGv]
}
```
### <span id="RandBool">RandBool</span>
<p>Generate a random boolean value (true or false).</p>
<b>Signature:</b>
```go
func RandBool() 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/random"
)
func main() {
result := random.RandBool()
fmt.Println(result) // true or false (random)
}
```
### <span id="RandBoolSlice">RandBoolSlice</span>
<p>Generates a random boolean slice of specified length.</p>
<b>Signature:</b>
```go
func RandBoolSlice(length int) []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/random"
)
func main() {
result := random.RandBoolSlice(2)
fmt.Println(result) // [true false] (random)
}
```

View File

@@ -27,6 +27,10 @@ import (
- [RetryFunc](#RetryFunc)
- [RetryDuration](#RetryDuration)
- [RetryTimes](#RetryTimes)
- [BackoffStrategy](#BackoffStrategy)
- [RetryWithCustomBackoff](#RetryWithCustomBackoff)
- [RetryWithLinearBackoff](#RetryWithLinearBackoff)
- [RetryWithExponentialWithJitterBackoff](#RetryWithExponentialWithJitterBackoff)
<div STYLE="page-break-after: always;"></div>
@@ -259,3 +263,202 @@ func main() {
// 3
}
```
### <span id="BackoffStrategy">BackoffStrategy</span>
<p>An interface that defines a method for calculating backoff intervals.</p>
<b>Signature:</b>
```go
// BackoffStrategy is an interface that defines a method for calculating backoff intervals.
type BackoffStrategy interface {
// CalculateInterval returns the time.Duration after which the next retry attempt should be made.
CalculateInterval() time.Duration
}
```
<b>Example:</b>
```go
package main
import (
"fmt"
"errors"
"log"
"github.com/duke-git/lancet/v2/retry"
)
type ExampleCustomBackoffStrategy struct {
interval time.Duration
}
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
return c.interval + 1
}
func main() {
number := 0
increaseNumber := func() error {
number++
if number == 3 {
return nil
}
return errors.New("error occurs")
}
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
```
### <span id="RetryWithCustomBackoff">RetryWithCustomBackoff</span>
<p>Set abitary custom backoff strategy.</p>
<b>Signature:</b>
```go
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jIm_o2vb5Y4)</span></b>
```go
package main
import (
"fmt"
"errors"
"log"
"github.com/duke-git/lancet/v2/retry"
)
type ExampleCustomBackoffStrategy struct {
interval time.Duration
}
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
return c.interval + 1
}
func main() {
number := 0
increaseNumber := func() error {
number++
if number == 3 {
return nil
}
return errors.New("error occurs")
}
err := retry.Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
```
### <span id="RetryWithLinearBackoff">RetryWithLinearBackoff</span>
<p>Set linear strategy backoff.</p>
<b>Signature:</b>
```go
func RetryWithLinearBackoff(interval time.Duration) Option
```
<b>Example:<span style="float:right;display:inline-block;">[Run](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")
}
err := retry.Retry(increaseNumber, retry.RetryWithLinearBackoff(time.Microsecond*50))
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
```
### <span id="RetryWithExponentialWithJitterBackoff">RetryWithExponentialWithJitterBackoff</span>
<p>Set exponential strategy backoff.</p>
<b>Signature:</b>
```go
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/xp1avQmn16X)</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")
}
err := retry.Retry(increaseNumber, retry.RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
```

View File

@@ -6,7 +6,8 @@ Package slice implements some functions to manipulate slice.
## Source:
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go](https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go)
<div STYLE="page-break-after: always;"></div>
@@ -44,6 +45,7 @@ import (
- [EqualWith](#EqualWith)
- [Every](#Every)
- [Filter](#Filter)
- [FilterConcurrent](#FilterConcurrent)
- [Find<sup>deprecated</sup>](#Find)
- [FindBy](#FindBy)
- [FindLast<sup>deprecated</sup>](#FindLast)
@@ -51,6 +53,7 @@ import (
- [Flatten](#Flatten)
- [FlattenDeep](#FlattenDeep)
- [ForEach](#ForEach)
- [ForEachConcurrent](#ForEachConcurrent)
- [ForEachWithBreak](#ForEachWithBreak)
- [GroupBy](#GroupBy)
- [GroupWith](#GroupWith)
@@ -61,11 +64,13 @@ import (
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [Map](#Map)
- [MapConcurrent](#MapConcurrent)
- [FilterMap](#FilterMap)
- [FlatMap](#FlatMap)
- [Merge](#Merge)
- [Reverse](#Reverse)
- [Reduce<sup>deprecated</sup>](#Reduce)
- [ReduceConcurrent](#ReduceConcurrent)
- [ReduceBy](#ReduceBy)
- [ReduceRight](#ReduceRight)
- [Replace](#Replace)
@@ -86,6 +91,9 @@ import (
- [ToSlicePointer](#ToSlicePointer)
- [Unique](#Unique)
- [UniqueBy](#UniqueBy)
- [UniqueByComparator](#UniqueByComparator)
- [UniqueByField](#UniqueByField)
- [UniqueByConcurrent](#UniqueByConcurrent)
- [Union](#Union)
- [UnionBy](#UnionBy)
- [UpdateAt](#UpdateAt)
@@ -93,6 +101,11 @@ import (
- [KeyBy](#KeyBy)
- [Join](#Join)
- [Partition](#Partition)
- [SetToDefaultIf](#SetToDefaultIf)
- [Break](#Break)
- [RightPadding](#RightPadding)
- [LeftPadding](#LeftPadding)
- [Frequency](#Frequency)
<div STYLE="page-break-after: always;"></div>
@@ -317,12 +330,12 @@ func main() {
### <span id="Concat">Concat</span>
<p>Creates a new slice concatenating slice with any additional slices.</p>
<p>Concat creates a new slice concatenating slice with any additional slices.</p>
<b>Signature:</b>
```go
func Concat[T any](slice []T, slices ...[]T) []T
func Concat[T any](slices ...[]T) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
@@ -524,7 +537,7 @@ func main() {
func DeleteAt[T any](slice []T, index int)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/pJ-d6MUWcvK)</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/800B1dPBYyd)</span></b>
```go
import (
@@ -563,7 +576,7 @@ func main() {
func DeleteRange[T any](slice []T, start, end int) []T
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/945HwiNrnle)</span></b>
```go
import (
@@ -574,11 +587,11 @@ import (
func main() {
chars := []string{"a", "b", "c", "d", "e"}
result1 := DeleteRange(chars, 0, 0)
result2 := DeleteRange(chars, 0, 1)
result3 := DeleteRange(chars, 0, 3)
result4 := DeleteRange(chars, 0, 4)
result5 := DeleteRange(chars, 0, 5)
result1 := slice.DeleteRange(chars, 0, 0)
result2 := slice.DeleteRange(chars, 0, 1)
result3 := slice.DeleteRange(chars, 0, 3)
result4 := slice.DeleteRange(chars, 0, 4)
result5 := slice.DeleteRange(chars, 0, 5)
fmt.Println(result1)
fmt.Println(result2)
@@ -890,10 +903,46 @@ func main() {
}
```
### <span id="Find">Find(deprecated: use FindBy)</span>
### <span id="FilterConcurrent">FilterConcurrent</span>
<p>Applies the provided filter function `predicate` to each element of the input slice concurrently.</p>
<b>Signature:</b>
```go
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T
```
<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() {
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
result := slice.FilterConcurrent(nums, isEven, 2)
fmt.Println(result)
// Output:
// [2 4]
}
```
### <span id="Find">Find</span>
<p>Iterates over elements of slice, returning the first one that passes a truth test on function.</p>
> ⚠️ This function is deprecated. use `FindBy` instead.
<b>Signature:</b>
```go
@@ -962,10 +1011,12 @@ func main() {
}
```
### <span id="FindLast">FindLast(deprecated: use FindLastBy)</span>
### <span id="FindLast">FindLast</span>
<p>iterates over elements of slice from end to begin, returning the last one that passes a truth test on function.</p>
> ⚠️ This function is deprecated. use `FindLastBy` instead.
<b>Signature:</b>
```go
@@ -1129,6 +1180,42 @@ func main() {
}
```
### <span id="ForEachConcurrent">ForEachConcurrent</span>
<p>Applies the iteratee function to each item in the slice concurrently.</p>
<b>Signature:</b>
```go
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads 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() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
result := make([]int, len(nums))
addOne := func(index int, value int) {
result[index] = value + 1
}
slice.ForEachConcurrent(nums, addOne, 4)
fmt.Println(result)
// Output:
// [2 3 4 5 6 7 8 9]
}
```
### <span id="ForEachWithBreak">ForEachWithBreak</span>
<p>Iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop.</p>
@@ -1237,10 +1324,12 @@ func main() {
}
```
### <span id="IntSlice">IntSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
### <span id="IntSlice">IntSlice</span>
<p>Convert interface slice to int slice.</p>
> ⚠️ This function is deprecated. Use generic feature of go1.18+ for replacement.
<b>Signature:</b>
```go
@@ -1266,10 +1355,12 @@ func main() {
}
```
### <span id="InterfaceSlice">InterfaceSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
### <span id="InterfaceSlice">InterfaceSlice</span>
<p>Convert value to interface slice.</p>
> ⚠️ This function is deprecated. Use generic feature of go1.18+ for replacement.
<b>Signature:</b>
```go
@@ -1466,6 +1557,36 @@ func main() {
}
```
### <span id="MapConcurrent">MapConcurrent</span>
<p>Applies the iteratee function to each item in the slice by concrrent.</p>
<b>Signature:</b>
```go
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6}
result := slice.MapConcurrent(nums, func(_, n int) int { return n * n }, 4)
fmt.Println(result)
// Output:
// [1 4 9 16 25 36]
}
```
### <span id="FilterMap">FilterMap</span>
<p>Returns a slice which apply both filtering and mapping to the given slice. iteratee callback function should returntwo values: 1, mapping result. 2, whether the result element should be included or not.</p>
@@ -1540,6 +1661,8 @@ func main() {
<p>Merge all given slices into one slice.</p>
> ⚠️ This function is deprecated. use `Concat` instead.
<b>Signature:</b>
```go
@@ -1599,7 +1722,9 @@ func main() {
### <span id="Reduce">Reduce</span>
<p>Reduce slice.(Deprecated: use ReduceBy)</p>
<p>Reduce slice.</p>
> ⚠️ This function is deprecated. use `ReduceBy` instead.
<b>Signature:</b>
@@ -1631,6 +1756,39 @@ func main() {
}
```
### <span id="ReduceConcurrent">ReduceConcurrent</span>
<p>Reduces the slice to a single value by applying the reducer function to each item in the slice concurrently.</p>
<b>Signature:</b>
```go
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;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := slice.ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 1)
fmt.Println(result)
// Output:
// 55
}
```
### <span id="ReduceBy">ReduceBy</span>
<p>Produces a value from slice by accumulating the result of each element as passed through the reducer function.</p>
@@ -2125,10 +2283,12 @@ func main() {
}
```
### <span id="StringSlice">StringSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
### <span id="StringSlice">StringSlice</span>
<p>Convert interface slice to string slice.</p>
> ⚠️ This function is deprecated. use generic feature of go1.18+ for replacement
<b>Signature:</b>
```go
@@ -2277,12 +2437,12 @@ func main() {
### <span id="UniqueBy">UniqueBy</span>
<p>Call iteratee func with every item of slice, then remove duplicated.</p>
<p>Removes duplicate elements from the input slice based on the values returned by the iteratee function. this function maintains the order of the elements.</p>
<b>Signature:</b>
```go
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/UR323iZLDpv)</span></b>
@@ -2302,7 +2462,114 @@ func main() {
fmt.Println(result)
// Output:
// [1 2 0]
// [1 2 3]
}
```
### <span id="UniqueByComparator">UniqueByComparator</span>
<p>Removes duplicate elements from the input slice using the provided comparator function. The function maintains the order of the elements.</p>
<b>Signature:</b>
```go
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
uniqueNums := slice.UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
return item == other
})
caseInsensitiveStrings := slice.UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool {
return strings.ToLower(item) == strings.ToLower(other)
})
fmt.Println(uniqueNums)
fmt.Println(caseInsensitiveStrings)
// Output:
// [1 2 3 4 5 6]
// [apple banana cherry date]
}
```
### <span id="UniqueByConcurrent">UniqueByConcurrent</span>
<p>Removes duplicate elements from the slice by parallel.</p>
<b>Signature:</b>
```go
func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool { return item == other }
result := slice.UniqueByConcurrent(nums,comparator, 4)
fmt.Println(result)
// Output:
// [1 2 3 4 5 6 7]
}
```
### <span id="UniqueByField">UniqueByField</span>
<p>Remove duplicate elements in struct slice by struct field.</p>
<b>Signature:</b>
```go
func UniqueByField[T any](slice []T, field string) ([]T, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Runs](https://go.dev/play/p/6cifcZSPIGu)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
users := []User{
{ID: 1, Name: "a"},
{ID: 2, Name: "b"},
{ID: 1, Name: "c"},
}
result, err := slice.UniqueByField(users, "ID")
if err != nil {
}
fmt.Println(result)
// Output:
// [{1 a} {2 b}]
}
```
@@ -2565,4 +2832,151 @@ func main() {
// Output:
// okk
}
```
### <span id="SetToDefaultIf">SetToDefaultIf</span>
<p>Sets elements to their default value if they match the given predicate. It retains the positions of the elements in the slice. It returns slice of T and the count of modified slice items</p>
<b>Signature:</b>
```go
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/9AXGlPRC0-A)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "a", "c", "d", "a"}
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
fmt.Println(modifiedStrs)
fmt.Println(count)
// Output:
// [ b c d ]
// 3
}
```
### <span id="Break">Break</span>
<p>Splits a slice into two based on a predicate function. It starts appending to the second slice after the first element that matches the predicate. All elements after the first match are included in the second slice, regardless of whether they match the predicate or not.</p>
<b>Signature:</b>
```go
func Break[T any](values []T, predicate func(T) bool) ([]T, []T)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/yLYcBTyeQIz)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
even := func(n int) bool { return n%2 == 0 }
resultEven, resultAfterFirstEven := slice.Break(nums, even)
fmt.Println(resultEven)
fmt.Println(resultAfterFirstEven)
// Output:
// [1]
// [2 3 4 5]
}
```
### <span id="RightPadding">RightPadding</span>
<p>RightPadding adds padding to the right end of a slice.</p>
<b>Signature:</b>
```go
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/0_2rlLEMBXL)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
}
```
### <span id="LeftPadding">LeftPadding</span>
<p>LeftPadding adds padding to the left begin of a slice.</p>
<b>Signature:</b>
```go
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jlQVoelLl2k)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
```
### <span id="Frequency">Frequency</span>
<p>Counts the frequency of each element in the slice.</p>
<b>Signature:</b>
```go
func Frequency[T comparable](slice []T) map[T]int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "b", "c", "c", "c"}
result := slice.Frequency(strs)
fmt.Println(result)
// Output:
// map[a:1 b:2 c:3]
}
```

View File

@@ -60,6 +60,14 @@ import (
- [ContainsAll](#ContainsAll)
- [ContainsAny](#ContainsAny)
- [RemoveWhiteSpace](#RemoveWhiteSpace)
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
- [Ellipsis](#Ellipsis)
- [Shuffle](#Shuffle)
- [Rotate](#Rotate)
- [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups)
<div STYLE="page-break-after: always;"></div>
@@ -1097,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)
@@ -1463,3 +1471,261 @@ func main() {
// hello world
}
```
### <span id="SubInBetween">SubInBetween</span>
<p>Return substring between the start and end position(excluded) of source string.</p>
<b>Signature:</b>
```go
func SubInBetween(str string, start string, end string) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/EDbaRvjeNsv)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
str := "abcde"
result1 := strutil.SubInBetween(str, "", "de")
result2 := strutil.SubInBetween(str, "a", "d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// abc
// bc
}
```
### <span id="HammingDistance">HammingDistance</span>
<p>HammingDistance calculates the Hamming distance between two strings. The Hamming distance is the number of positions at which the corresponding symbols are different.</p>
<b>Signature:</b>
```go
func HammingDistance(a, b string) (int, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/glNdQEA9HUi)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1, _ := strutil.HammingDistance("de", "de")
result2, _ := strutil.HammingDistance("a", "d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 0
// 1
}
```
### <span id="Concat">Concat</span>
<p>Concatenates strings. <b>length</b> is the length of the concatenated string. If unsure, pass 0 or a negative number.</p>
<b>Signature:</b>
```go
func Concat(length int, 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() {
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}
```
### <span id="Ellipsis">Ellipsis</span>
<p>Truncates a string to a specified length and appends an ellipsis.</p>
<b>Signature:</b>
```go
func Ellipsis(str string, length int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Ellipsis("hello world", 5)
result2 := strutil.Ellipsis("你好,世界!", 2)
result3 := strutil.Ellipsis("😀😃😄😁😆", 3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// hello...
// 你好...
// 😀😃😄...
}
```
### <span id="Shuffle">Shuffle</span>
<p>Shuffle the order of characters of given string.</p>
<b>Signature:</b>
```go
func Shuffle(str string) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result := strutil.Shuffle("hello")
fmt.Println(result) //olelh (random order)
}
```
### <span id="Rotate">Rotate</span>
<p>Rotates the string by the specified number of characters.</p>
<b>Signature:</b>
```go
func Rotate(str string, shift int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := Rotate("Hello", 0)
result2 := Rotate("Hello", 1)
result3 := Rotate("Hello", 2)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello
// oHell
// loHel
}
```
### <span id="TemplateReplace">TemplateReplace</span>
<p>Replaces the placeholders in the template string with the corresponding values in the data map.The placeholders are enclosed in curly braces, e.g. {key}. for example, the template string is "Hello, {name}!", and the data map is {"name": "world"}, the result will be "Hello, world!".</p>
<b>Signature:</b>
```go
func TemplateReplace(template string, data map[string]string string
```
<b>example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
result := strutil.TemplateReplace(template, data)
fmt.Println(result)
// Output:
// Hello, my name is Bob, I'm 20 years old.
}
```
### <span id="RegexMatchAllGroups">RegexMatchAllGroups</span>
<p>Matches all subgroups in a string using a regular expression and returns the result.</p>
<b>Signature:</b>
```go
func RegexMatchAllGroups(pattern, str string) [][]string
```
<b>example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
str := "Emails: john.doe@example.com and jane.doe@example.com"
result := strutil.RegexMatchAllGroups(pattern, str)
fmt.Println(result[0])
fmt.Println(result[1])
// Output:
// [john.doe@example.com john.doe example com]
// [jane.doe@example.com jane.doe example com]
}
```

View File

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

View File

@@ -30,7 +30,7 @@ We are excited that you are interested in contributing to lancet. Before submitt
- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass.
- Make sure PRs are created to `v2` branch instead of `master` branch.
- Make sure PRs are created to `rc` branch instead of other branch.
- If your PR fixes a bug, please provide a description about the related bug.

View File

@@ -33,7 +33,7 @@ features:
details: Well structure, test for every exported function.
---
<p style="position:relative; top: -316px;left: 560px;">
<p style="position:relative; inline-block;top: -330px;left: 30%">
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
</p>

View File

@@ -30,7 +30,7 @@ Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代
- 提交 PR 前请执行单元测试命令go test -v ./...,确保所有单元测试任务通过。
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
- 确保 PR 是提交到 `rc` 分支,而不是其他分支。
- 如果是修复 bug请在 PR 中给出描述信息。

View File

@@ -33,7 +33,7 @@ features:
details: 结构良好,测试每个导出的函数。
---
<p style="position:relative;display: inline-block;top: -316px;left: 32%">
<p style="position:relative;display: inline-block;top: -330px;left: 30%">
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
</p>

1171
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,6 @@
"docs:preview": "vitepress preview"
},
"devDependencies": {
"vitepress": "^1.0.0-rc.4"
"vitepress": "^1.2.3"
}
}

View File

@@ -22,6 +22,7 @@ import (
"runtime"
"sort"
"strings"
"sync"
"github.com/duke-git/lancet/v2/validator"
)
@@ -65,8 +66,8 @@ func (f *FileReader) Offset() int64 {
return f.offset
}
// Seek sets the current offset of the reading
func (f *FileReader) Seek(offset int64) error {
// SeekOffset sets the current offset of the reading
func (f *FileReader) SeekOffset(offset int64) error {
_, err := f.file.Seek(offset, 0)
if err != nil {
return err
@@ -113,6 +114,50 @@ func CreateDir(absPath string) error {
return os.MkdirAll(absPath, os.ModePerm)
}
// CopyDir copy src directory to dst directory, it will copy all files and directories recursively.
// the access permission will be the same as the source directory.
// if dstPath exists, it will return an error.
// Play: https://go.dev/play/p/YAyFTA_UuPb
func CopyDir(srcPath string, dstPath string) error {
srcInfo, err := os.Stat(srcPath)
if err != nil {
return fmt.Errorf("failed to get source directory info: %w", err)
}
if !srcInfo.IsDir() {
return fmt.Errorf("source path is not a directory: %s", srcPath)
}
err = os.MkdirAll(dstPath, 0755)
if err != nil {
return fmt.Errorf("failed to create destination directory: %w", err)
}
entries, err := os.ReadDir(srcPath)
if err != nil {
return fmt.Errorf("failed to read source directory: %w", err)
}
for _, entry := range entries {
srcDir := filepath.Join(srcPath, entry.Name())
dstDir := filepath.Join(dstPath, entry.Name())
if entry.IsDir() {
err := CopyDir(srcDir, dstDir)
if err != nil {
return err
}
} else {
err := CopyFile(srcDir, dstDir)
if err != nil {
return err
}
}
}
return nil
}
// IsDir checks if the path is directory or not.
// Play: https://go.dev/play/p/WkVwEKqtOWk
func IsDir(path string) bool {
@@ -377,7 +422,7 @@ func UnZip(zipFile string, destPath string) error {
defer zipReader.Close()
for _, f := range zipReader.File {
//issue#62: fix ZipSlip bug
// issue#62: fix ZipSlip bug
path, err := safeFilepathJoin(destPath, f.Name)
if err != nil {
return err
@@ -754,7 +799,7 @@ func escapeCSVField(field string, delimiter rune) string {
}
// WriteMapsToCsv write slice of map to csv file.
// Play: todo
// Play: https://go.dev/play/p/umAIomZFV1c
// filepath: Path to the CSV file.
// records: Slice of maps to be written. the value of map should be basic type.
// the maps will be sorted by key in alphabeta order, then be written into csv file.
@@ -807,3 +852,98 @@ func isCsvSupportedType(v interface{}) bool {
return false
}
}
// ChunkRead reads a block from the file at the specified offset and returns all lines within the block
// Play: https://go.dev/play/p/r0hPmKWhsgf
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error) {
buf := bufPool.Get().([]byte)[:size] // 从Pool获取缓冲区并调整大小
n, err := file.ReadAt(buf, offset) // 从指定偏移读取数据到缓冲区
if err != nil && err != io.EOF {
return nil, err
}
buf = buf[:n] // 调整切片以匹配实际读取的字节数
var lines []string
var lineStart int
for i, b := range buf {
if b == '\n' {
line := string(buf[lineStart:i]) // 不包括换行符
lines = append(lines, line)
lineStart = i + 1 // 设置下一行的开始
}
}
if lineStart < len(buf) { // 处理块末尾的行
line := string(buf[lineStart:])
lines = append(lines, line)
}
bufPool.Put(buf) // 读取完成后将缓冲区放回Pool
return lines, nil
}
// ParallelChunkRead reads the file in parallel and send each chunk of lines to the specified channel.
// filePath 文件路径
// chunkSizeMB 分块的大小单位MB设置为0时使用默认100MB,设置过大反而不利,视情调整
// maxGoroutine 并发读取分块的数量设置为0时使用CPU核心数
// linesCh用于接收返回结果的通道。
// Play: https://go.dev/play/p/teMXnCsdSEw
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error {
if chunkSizeMB == 0 {
chunkSizeMB = 100
}
chunkSize := chunkSizeMB * 1024 * 1024
// 内存复用
bufPool := sync.Pool{
New: func() interface{} {
return make([]byte, 0, chunkSize)
},
}
if maxGoroutine == 0 {
maxGoroutine = runtime.NumCPU() // 设置为0时使用CPU核心数
}
f, err := os.Open(filePath)
if err != nil {
return err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
return err
}
wg := sync.WaitGroup{}
chunkOffsetCh := make(chan int64, maxGoroutine)
// 分配工作
go func() {
for i := int64(0); i < info.Size(); i += int64(chunkSize) {
chunkOffsetCh <- i
}
close(chunkOffsetCh)
}()
// 启动工作协程
for i := 0; i < maxGoroutine; i++ {
wg.Add(1)
go func() {
for chunkOffset := range chunkOffsetCh {
chunk, err := ChunkRead(f, chunkOffset, chunkSize, &bufPool)
if err == nil {
linesCh <- chunk
}
}
wg.Done()
}()
}
// 等待所有解析完成后关闭行通道
wg.Wait()
close(linesCh)
return nil
}

View File

@@ -5,6 +5,8 @@ import (
"io"
"log"
"os"
"runtime"
"sync"
)
func ExampleIsExist() {
@@ -421,8 +423,69 @@ func ExampleReadFile() {
if err != nil {
return
}
fmt.Println(string(dat))
// Output:
// User-agent: *
// Disallow: /deny
}
func ExampleChunkRead() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100
filePath := "./testdata/test1.csv"
f, err := os.Open(filePath)
if err != nil {
return
}
defer f.Close()
var bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultChunkSizeMB*mb)
},
}
lines, err := ChunkRead(f, 0, 100, &bufPool)
if err != nil {
return
}
fmt.Println(lines[0])
fmt.Println(lines[1])
// Output:
// Lili,22,female
// Jim,21,male
}
func ExampleParallelChunkRead() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
filePath := "./testdata/test1.csv"
go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
var totalLines int
for lines := range linesCh {
totalLines += len(lines)
for _, line := range lines {
fmt.Println(line)
}
}
fmt.Println(totalLines)
// Output:
// Lili,22,female
// Jim,21,male
// 2
}

View File

@@ -3,6 +3,10 @@ package fileutil
import (
"io"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"testing"
"github.com/duke-git/lancet/v2/internal"
@@ -534,7 +538,7 @@ func TestReadlineFile(t *testing.T) {
if !ok {
t.Fail()
}
if err = reader.Seek(offset); err != nil {
if err = reader.SeekOffset(offset); err != nil {
t.Fail()
}
lineRead, err := reader.ReadLine()
@@ -544,3 +548,74 @@ func TestReadlineFile(t *testing.T) {
internal.NewAssert(t, "TestReadlineFile").Equal(line, lineRead)
}
}
func TestCopyDir(t *testing.T) {
assert := internal.NewAssert(t, "TestCopyDir")
src := "./testdata"
dest := "./testdata_copy"
err := CopyDir(src, dest)
assert.IsNil(err)
assert.Equal(true, IsExist(dest))
filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
destPath := strings.Replace(path, src, dest, 1)
assert.Equal(true, IsExist(destPath))
return nil
})
os.RemoveAll(dest)
}
func TestParallelChunkRead(t *testing.T) {
assert := internal.NewAssert(t, "TestParallelChunkRead")
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
filePath := "./testdata/test1.csv" // 替换为你的文件路径
go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
var totalLines int
for lines := range linesCh {
totalLines += len(lines)
assert.Equal("Lili,22,female", lines[0])
assert.Equal("Jim,21,male", lines[1])
}
assert.Equal(2, totalLines)
}
func TestChunkRead(t *testing.T) {
assert := internal.NewAssert(t, "TestChunkRead")
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
filePath := "./testdata/test1.csv" // 替换为你的文件路径
f, err := os.Open(filePath)
if err != nil {
return
}
defer f.Close()
var bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultChunkSizeMB*mb)
},
}
lines, err := ChunkRead(f, 0, 100, &bufPool)
assert.Equal("Lili,22,female", lines[0])
assert.Equal("Jim,21,male", lines[1])
}

1
fileutil/testdata/test01/demo2.csv vendored Normal file
View File

@@ -0,0 +1 @@
makj1
1 makj1

View File

@@ -1,5 +1,2 @@
Lili,22,female
Jim,21,male
1 Lili 22 female
2 Jim 21 male

View File

@@ -6,6 +6,9 @@ import (
"strconv"
"strings"
"unicode"
"github.com/duke-git/lancet/v2/mathutil"
"github.com/duke-git/lancet/v2/strutil"
)
//
@@ -15,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,
}
)
@@ -84,26 +87,28 @@ var (
// The precision parameter specifies the number of digits after the decimal point, which defaults to 4.
// Play: https://go.dev/play/p/FPXs1suwRcs
func DecimalBytes(size float64, precision ...int) string {
p := 5
pointPosition := 4
if len(precision) > 0 {
p = precision[0] + 1
pointPosition = precision[0]
}
size, unit := calculateByteSize(size, 1000.0, decimalByteUnits)
return fmt.Sprintf("%.*g%s", p, size, unit)
return roundToToString(size, pointPosition) + unit
}
// BinaryBytes returns a human-readable byte size under binary standard (base 1024)
// The precision parameter specifies the number of digits after the decimal point, which defaults to 4.
// Play: https://go.dev/play/p/G9oHHMCAZxP
func BinaryBytes(size float64, precision ...int) string {
p := 5
pointPosition := 4
if len(precision) > 0 {
p = precision[0] + 1
pointPosition = precision[0]
}
size, unit := calculateByteSize(size, 1024.0, binaryByteUnits)
return fmt.Sprintf("%.*g%s", p, size, unit)
return roundToToString(size, pointPosition) + unit
}
func calculateByteSize(size float64, base float64, byteUnits []string) (float64, string) {
@@ -116,6 +121,29 @@ func calculateByteSize(size float64, base float64, byteUnits []string) (float64,
return size, byteUnits[i]
}
func roundToToString(x float64, max ...int) string {
pointPosition := 4
if len(max) > 0 {
pointPosition = max[0]
}
result := mathutil.RoundToString(x, pointPosition)
// 删除小数位结尾的0
decimal := strings.TrimRight(strutil.After(result, "."), "0")
if decimal == "" || pointPosition == 0 {
// 没有小数位直接返回整数
return strutil.Before(result, ".")
}
// 小数位大于想要设置的位数,按需要截断
if len(decimal) > pointPosition {
return strutil.Before(result, ".") + "." + decimal[:pointPosition]
}
// 小数位小于等于想要的位数,直接拼接返回
return strutil.Before(result, ".") + "." + decimal
}
// ParseDecimalBytes return the human readable bytes size string into the amount it represents(base 1000).
// ParseDecimalBytes("42 MB") -> 42000000, nil
// Play: https://go.dev/play/p/Am98ybWjvjj

View File

@@ -15,11 +15,19 @@ 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))
assert.Equal("401MB", DecimalBytes(401000000, 0))
assert.Equal("4MB", DecimalBytes(4010000, 0))
assert.Equal("4MB", DecimalBytes(4000000, 0))
assert.Equal("4MB", DecimalBytes(4000000, 1))
}
func TestBinaryBytes(t *testing.T) {
@@ -31,6 +39,10 @@ func TestBinaryBytes(t *testing.T) {
assert.Equal("1MiB", BinaryBytes(1024*1024))
assert.Equal("1.1774MiB", BinaryBytes(1234567))
assert.Equal("1.18MiB", BinaryBytes(1234567, 2))
assert.Equal("10KiB", BinaryBytes(10240, 0))
assert.Equal("10KiB", BinaryBytes(10240, 1))
assert.Equal("10KiB", BinaryBytes(10240, 2))
}
func TestParseDecimalBytes(t *testing.T) {

View File

@@ -14,11 +14,11 @@ import (
"golang.org/x/exp/constraints"
)
// Comma add comma to a number value by every 3 numbers from right. ahead by symbol char.
// Comma add comma to a number value by every 3 numbers from right. ahead by prefix symbol char.
// if value is invalid number string eg "aa", return empty string
// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345"
// Play: https://go.dev/play/p/eRD5k2vzUVX
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string {
func Comma[T constraints.Float | constraints.Integer | string](value T, prefixSymbol string) string {
numString := convertor.ToString(value)
_, err := strconv.ParseFloat(numString, 64)
@@ -26,17 +26,26 @@ func Comma[T constraints.Float | constraints.Integer | string](value T, symbol s
return ""
}
isNegative := strings.HasPrefix(numString, "-")
if isNegative {
numString = numString[1:]
}
index := strings.Index(numString, ".")
if index == -1 {
index = len(numString)
}
for index > 3 {
index = index - 3
index -= 3
numString = numString[:index] + "," + numString[index:]
}
return symbol + numString
if isNegative {
numString = "-" + numString
}
return prefixSymbol + numString
}
// Pretty data to JSON string.

View File

@@ -28,6 +28,10 @@ func TestComma(t *testing.T) {
assert.Equal("12,345.6789", Comma(+12345.6789, ""))
assert.Equal("12,345,678.9", Comma(12345678.9, ""))
assert.Equal("123,456,789,000", Comma(123456789000, ""))
assert.Equal("-999", Comma(-999, ""))
assert.Equal("-1,000", Comma(-1000, ""))
assert.Equal("-1,234,567", Comma(-1234567, ""))
}
func TestPretty(t *testing.T) {

View File

@@ -7,6 +7,7 @@ package function
import (
"fmt"
"reflect"
"sync"
"time"
)
@@ -84,36 +85,97 @@ func Delay(delay time.Duration, fn any, args ...any) {
}
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
// Deprecated: Use Debounce function instead.
// Play: https://go.dev/play/p/absuEGB_GN7
func Debounced(fn func(), duration time.Duration) func() {
// Catch programming error while constructing the closure
mustBeFunction(fn)
func Debounced(fn func(), delay time.Duration) func() {
debouncedFn, _ := Debounce(fn, delay)
return debouncedFn
}
timer := time.NewTimer(duration)
timer.Stop()
// Debounce creates a debounced version of the provided function.
// Play: todo
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func()) {
var (
timer *time.Timer
mu sync.Mutex
)
go func() {
for {
<-timer.C
go fn()
debouncedFn = func() {
mu.Lock()
defer mu.Unlock()
if timer != nil {
timer.Stop()
}
}()
return func() { timer.Reset(duration) }
timer = time.AfterFunc(delay, func() {
fn()
})
}
cancelFn = func() {
mu.Lock()
defer mu.Unlock()
if timer != nil {
timer.Stop()
}
}
return debouncedFn, cancelFn
}
// Throttle creates a throttled version of the provided function.
// The returned function guarantees that it will only be invoked at most once per interval.
// Play: todo
func Throttle(fn func(), interval time.Duration) func() {
var (
timer *time.Timer
lastRun time.Time
mu sync.Mutex
)
return func() {
mu.Lock()
defer mu.Unlock()
now := time.Now()
if now.Sub(lastRun) >= interval {
fn()
lastRun = now
if timer != nil {
timer.Stop()
timer = nil
}
} else if timer == nil {
delay := interval - now.Sub(lastRun)
timer = time.AfterFunc(delay, func() {
mu.Lock()
defer mu.Unlock()
fn()
lastRun = time.Now()
timer = nil
})
}
}
}
// Schedule invoke function every duration time, util close the returned bool channel.
// Play: https://go.dev/play/p/hbON-Xeyn5N
func Schedule(d time.Duration, fn any, args ...any) chan bool {
func Schedule(duration time.Duration, fn any, args ...any) chan bool {
// Catch programming error while constructing the closure
mustBeFunction(fn)
quit := make(chan bool)
go func() {
for {
unsafeInvokeFunc(fn, args...)
select {
case <-time.After(d):
case <-time.After(duration):
case <-quit:
return
}
@@ -136,6 +198,28 @@ func Pipeline[T any](funcs ...func(T) T) func(T) T {
}
}
// AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
// A predicate function that takes an argument of type T and returns a bool.
// An apply function that also takes an argument of type T and returns a modified value of the same type.
// Play: https://go.dev/play/p/XlXHHtzCf7d
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool) {
if predicate == nil {
panic("programming error: predicate must be not nil")
}
if apply == nil {
panic("programming error: apply must be not nil")
}
return func(t T) (T, bool) {
if !predicate(t) {
var defaultValue T
return defaultValue, false
}
return apply(t), true
}
}
func unsafeInvokeFunc(fn any, args ...any) []reflect.Value {
fv := reflect.ValueOf(fn)
params := make([]reflect.Value, len(args))

View File

@@ -79,6 +79,32 @@ func ExampleDelay() {
// hello
}
func ExampleDebounce() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
func ExampleDebounced() {
count := 0
add := func() {
@@ -145,3 +171,53 @@ func ExamplePipeline() {
// Output:
// 36
}
func ExampleAcceptIf() {
adder := AcceptIf(
And(
func(x int) bool {
return x > 10
}, func(x int) bool {
return x%2 == 0
}),
func(x int) int {
return x + 1
},
)
result, ok := adder(20)
fmt.Println(result)
fmt.Println(ok)
result, ok = adder(21)
fmt.Println(result)
fmt.Println(ok)
// Output:
// 21
// true
// 0
// false
}
func ExampleThrottle() {
callCount := 0
fn := func() {
callCount++
}
throttledFn := Throttle(fn, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
}

View File

@@ -47,7 +47,7 @@ func TestBefore(t *testing.T) {
var res []int64
type cb func(args ...any) []reflect.Value
appendStr := func(i int, s string, fn cb) {
appendStr := func(i int, _ string, fn cb) {
v := fn(i)
res = append(res, v[0].Int())
}
@@ -125,24 +125,178 @@ func TestDebounced(t *testing.T) {
assert.Equal(2, count)
}
func TestDebounce(t *testing.T) {
assert := internal.NewAssert(t, "TestDebounce")
t.Run("Single call", func(t *testing.T) {
callCount := 0
debouncedFn, _ := Debounce(func() {
callCount++
}, 500*time.Millisecond)
debouncedFn()
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Multiple calls within debounce interval", func(t *testing.T) {
callCount := 0
debouncedFn, _ := Debounce(func() {
callCount++
}, 1*time.Second)
for i := 0; i < 5; i++ {
go func(index int) {
time.Sleep(time.Duration(index) * 200 * time.Millisecond)
debouncedFn()
}(i)
}
time.Sleep(2 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Immediate consecutive calls", func(t *testing.T) {
callCount := 0
debouncedFn, _ := Debounce(func() {
callCount++
}, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Cancel calls", func(t *testing.T) {
callCount := 0
debouncedFn, cancelFn := Debounce(func() {
callCount++
}, 500*time.Millisecond)
debouncedFn()
cancelFn()
time.Sleep(1 * time.Second)
assert.Equal(0, callCount)
})
}
func TestThrottle(t *testing.T) {
assert := internal.NewAssert(t, "TestThrottle")
t.Run("Single call", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 1*time.Second)
throttledFn()
time.Sleep(100 * time.Millisecond)
assert.Equal(1, callCount)
})
t.Run("Multiple calls within throttle interval", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Mutiple calls space out throttle interval", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 500*time.Millisecond)
throttledFn()
time.Sleep(600 * time.Millisecond)
throttledFn()
time.Sleep(600 * time.Millisecond)
throttledFn()
time.Sleep(1 * time.Second)
assert.Equal(3, callCount)
})
t.Run("Call function near the end of the interval", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 1*time.Second)
throttledFn()
time.Sleep(900 * time.Millisecond)
throttledFn()
time.Sleep(200 * time.Millisecond)
assert.Equal(2, callCount)
})
}
func TestSchedule(t *testing.T) {
// assert := internal.NewAssert(t, "TestSchedule")
assert := internal.NewAssert(t, "TestSchedule")
var res []string
appendStr := func(s string) {
res = append(res, s)
}
t.Run("Single call", func(t *testing.T) {
res := []string{}
appendStr := func(s string) {
res = append(res, s)
}
stop := Schedule(200*time.Millisecond, appendStr, "*")
close(stop)
stop := Schedule(1*time.Second, appendStr, "*")
time.Sleep(5 * time.Second)
close(stop)
time.Sleep(400 * time.Millisecond)
t.Log(res)
assert.Equal([]string{"*"}, res)
})
t.Run("Multiple calls", func(t *testing.T) {
res := []string{}
appendStr := func(s string) {
res = append(res, s)
}
stop := Schedule(200*time.Millisecond, appendStr, "*")
time.Sleep(800 * time.Millisecond)
close(stop)
assert.Equal([]string{"*", "*", "*", "*"}, res)
})
// todo: in github action, for now, this test is not working sometimes
// res maybe [* * * * *] or [* * * * * *]
// expected := []string{"*", "*", "*", "*", "*"}
// assert.Equal(expected, res)
}
func TestPipeline(t *testing.T) {
@@ -162,3 +316,63 @@ func TestPipeline(t *testing.T) {
assert.Equal(36, f(2))
}
func TestAcceptIf(t *testing.T) {
assert := internal.NewAssert(t, "AcceptIf")
adder := AcceptIf(
And(
func(x int) bool {
return x > 10
}, func(x int) bool {
return x%2 == 0
}),
func(x int) int {
return x + 1
},
)
result, ok := adder(20)
assert.Equal(21, result)
assert.Equal(true, ok)
result, ok = adder(21)
assert.Equal(0, result)
assert.Equal(false, ok)
}
func TestAcceptIfPanicMissingPredicate(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAcceptIfPanicMissingPredicate")
defer func() {
v := recover()
assert.Equal("programming error: predicate must be not nil", v)
}()
AcceptIf(
nil,
func(x int) int {
return x
},
)
}
func TestAcceptIfPanicMissingApply(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAcceptIfPanicMissingApply")
defer func() {
v := recover()
assert.Equal("programming error: apply must be not nil", v)
}()
AcceptIf(
func(i int) bool {
return false
},
nil,
)
}

97
function/predicate.go Normal file
View File

@@ -0,0 +1,97 @@
package function
// And returns a composed predicate that represents the logical AND of a list of predicates.
// It evaluates to true only if all predicates evaluate to true for the given value.
// Play: https://go.dev/play/p/dTBHJMQ0zD2
func And[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if !predicate(value) {
return false // Short-circuit on the first false predicate
}
}
return true // True if all predicates are true
}
}
// Nand returns a composed predicate that represents the logical NAND of a list of predicates.
// It evaluates to true only if all predicates evaluate to false for the given value.
// Play: https://go.dev/play/p/Rb-FdNGpgSO
func Nand[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if predicate(value) {
return false // Short-circuit on the first true predicate
}
}
return true // True if all predicates are false
}
}
// Negate returns a predicate that represents the logical negation of this predicate.
// Play: https://go.dev/play/p/jbI8BtgFnVE
func Negate[T any](predicate func(T) bool) func(T) bool {
return func(value T) bool {
return !predicate(value)
}
}
// Or returns a composed predicate that represents the logical OR of a list of predicates.
// It evaluates to true if at least one of the predicates evaluates to true for the given value.
// Play: https://go.dev/play/p/LitCIsDFNDA
func Or[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if predicate(value) {
return true // Short-circuit on the first true predicate
}
}
return false // False if all predicates are false
}
}
// Nor returns a composed predicate that represents the logical NOR of a list of predicates.
// It evaluates to true only if all predicates evaluate to false for the given value.
// Play: https://go.dev/play/p/2KdCoBEOq84
func Nor[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if predicate(value) {
return false // If any predicate evaluates to true, the NOR result is false
}
}
return true // Only returns true if all predicates evaluate to false
}
}
// Xnor returns a composed predicate that represents the logical XNOR of a list of predicates.
// It evaluates to true only if all predicates evaluate to true or false for the given value.
// Play: https://go.dev/play/p/FJxko8SFbqc
func Xnor[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
trueCount := 0
for _, predicate := range predicates {
if predicate(value) {
trueCount++
}
}
// XNOR is true if either all predicates are true or all are false
// This is the same as saying trueCount is 0 (all false) or trueCount is len(predicates) (all true)
return trueCount == 0 || trueCount == len(predicates)
}
}

View File

@@ -0,0 +1,128 @@
package function
import (
"fmt"
"strings"
)
func ExampleNegate() {
// Define some simple predicates for demonstration
isUpperCase := func(s string) bool {
return strings.ToUpper(s) == s
}
isLowerCase := func(s string) bool {
return strings.ToLower(s) == s
}
isMixedCase := Negate(Or(isUpperCase, isLowerCase))
fmt.Println(isMixedCase("ABC"))
fmt.Println(isMixedCase("AbC"))
// Output:
// false
// true
}
func ExampleOr() {
containsDigitOrSpecialChar := Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
)
fmt.Println(containsDigitOrSpecialChar("hello!"))
fmt.Println(containsDigitOrSpecialChar("hello"))
// Output:
// true
// false
}
func ExampleAnd() {
isNumericAndLength5 := And(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcde"))
// Output:
// true
// false
// false
}
func ExampleNand() {
isNumericAndLength5 := Nand(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcdef"))
// Output:
// false
// false
// true
}
func ExampleNor() {
match := Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("dbcdckkeee"))
match = Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("0123456789"))
// Output:
// true
// false
}
func ExampleXnor() {
isEven := func(i int) bool { return i%2 == 0 }
isPositive := func(i int) bool { return i > 0 }
match := Xnor(isEven, isPositive)
fmt.Println(match(2))
fmt.Println(match(-3))
fmt.Println(match(3))
// Output:
// true
// true
// false
}
// func ExamplePredicatesMix() {
// a := Or(
// func(s string) bool { return strings.ContainsAny(s, "0123456789") },
// func(s string) bool { return strings.ContainsAny(s, "!") },
// )
// b := And(
// func(s string) bool { return strings.ContainsAny(s, "hello") },
// func(s string) bool { return strings.ContainsAny(s, "!") },
// )
// c := Negate(And(a, b))
// fmt.Println(c("hello!"))
// c = Nor(a, b)
// fmt.Println(c("hello!"))
// // Output:
// // false
// // false
// }

134
function/predicate_test.go Normal file
View File

@@ -0,0 +1,134 @@
package function
import (
"strings"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestPredicatesNegatePure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesNegatePure")
// Define some simple predicates for demonstration
isUpperCase := func(s string) bool {
return strings.ToUpper(s) == s
}
isLowerCase := func(s string) bool {
return strings.ToLower(s) == s
}
isMixedCase := Negate(Or(isUpperCase, isLowerCase))
assert.ShouldBeFalse(isMixedCase("ABC"))
assert.ShouldBeTrue(isMixedCase("AbC"))
}
func TestPredicatesOrPure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesOrPure")
containsDigitOrSpecialChar := Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
)
assert.ShouldBeTrue(containsDigitOrSpecialChar("hello!"))
assert.ShouldBeFalse(containsDigitOrSpecialChar("hello"))
}
func TestPredicatesAndPure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesAndPure")
isNumericAndLength5 := And(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
assert.ShouldBeTrue(isNumericAndLength5("12345"))
assert.ShouldBeFalse(isNumericAndLength5("1234"))
assert.ShouldBeFalse(isNumericAndLength5("abcde"))
}
func TestPredicatesNandPure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesNandPure")
isNumericAndLength5 := Nand(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
assert.ShouldBeFalse(isNumericAndLength5("12345"))
assert.ShouldBeFalse(isNumericAndLength5("1234"))
assert.ShouldBeTrue(isNumericAndLength5("abcdef"))
}
func TestPredicatesNorPure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesNorPure")
match := Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
assert.ShouldBeTrue(match("dbcdckkeee"))
match = Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
assert.ShouldBeFalse(match("0123456789"))
}
func TestPredicatesXnorPure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesXnorPure")
isEven := func(i int) bool { return i%2 == 0 }
isPositive := func(i int) bool { return i > 0 }
match := Xnor(isEven, isPositive)
assert.ShouldBeTrue(match(2))
assert.ShouldBeTrue(match(-3))
assert.ShouldBeFalse(match(3))
}
func TestPredicatesMix(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesMix")
a := Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!") },
)
b := And(
func(s string) bool { return strings.ContainsAny(s, "hello") },
func(s string) bool { return strings.ContainsAny(s, "!") },
)
c := Negate(And(a, b))
assert.ShouldBeFalse(c("hello!"))
k := Nor(a, b)
assert.ShouldBeFalse(k("hello!"))
o := Xnor(a, b)
assert.ShouldBeTrue(o("hello!"))
p := Nand(c, k)
assert.ShouldBeTrue(p("hello!"))
}

View File

@@ -37,6 +37,20 @@ func (a *Assert) Equal(expected, actual any) {
}
}
// ShouldBeFalse check if expected is false
func (a *Assert) ShouldBeFalse(actual any) {
if compare(false, actual) != compareEqual {
makeTestFailed(a.T, a.CaseName, false, actual)
}
}
// ShouldBeTrue check if expected is true
func (a *Assert) ShouldBeTrue(actual any) {
if compare(true, actual) != compareEqual {
makeTestFailed(a.T, a.CaseName, true, actual)
}
}
// NotEqual check if expected is not equal with actual
func (a *Assert) NotEqual(expected, actual any) {
if compare(expected, actual) == compareEqual {

View File

@@ -6,10 +6,11 @@ import (
func TestAssert(t *testing.T) {
assert := NewAssert(t, "TestAssert")
assert.Equal(0, 0)
assert.NotEqual(1, 0)
assert.NotEqual("1", 1)
var uInt1 uint
var uInt2 uint
var uInt8 uint8
@@ -47,4 +48,11 @@ func TestAssert(t *testing.T) {
assert.IsNil(nil)
assert.IsNotNil("abc")
var valA int = 1
var valB int64 = 1
assert.NotEqual(valA, valB)
assert.EqualValues(valA, valB)
assert.ShouldBeFalse(false)
assert.ShouldBeTrue(true)
}

View File

@@ -27,7 +27,15 @@ type Iterator[T any] interface {
Next() (item T, ok bool)
}
// StopIterator is an interface for stopping Iterator.
// ResettableIterator supports to reset the iterator
type ResettableIterator[T any] interface {
Iterator[T]
// Reset allows for the iteration process over a sequence to be restarted from the beginning.
// It enables reusing the iterator for multiple traversals without needing to recreate it.
Reset()
}
// StopIterator is an interface for stopping Iterator.
type StopIterator[T any] interface {
Iterator[T]
@@ -81,8 +89,8 @@ type PrevIterator[T any] interface {
////////////////////////////////////////////////////////////////////////////////////////////////////
// FromSlice returns an iterator over a slice of data.
func FromSlice[T any](slice []T) Iterator[T] {
return &sliceIterator[T]{slice: slice, index: -1}
func FromSlice[T any](slice []T) *SliceIterator[T] {
return &SliceIterator[T]{slice: slice, index: -1}
}
func ToSlice[T any](iter Iterator[T]) []T {
@@ -93,16 +101,16 @@ func ToSlice[T any](iter Iterator[T]) []T {
return result
}
type sliceIterator[T any] struct {
type SliceIterator[T any] struct {
slice []T
index int
}
func (iter *sliceIterator[T]) HasNext() bool {
func (iter *SliceIterator[T]) HasNext() bool {
return iter.index < len(iter.slice)-1
}
func (iter *sliceIterator[T]) Next() (T, bool) {
func (iter *SliceIterator[T]) Next() (T, bool) {
iter.index++
ok := iter.index >= 0 && iter.index < len(iter.slice)
@@ -116,7 +124,7 @@ func (iter *sliceIterator[T]) Next() (T, bool) {
}
// Prev implements PrevIterator.
func (iter *sliceIterator[T]) Prev() {
func (iter *SliceIterator[T]) Prev() {
if iter.index == -1 {
panic("Next function should be called Prev")
}
@@ -128,7 +136,7 @@ func (iter *sliceIterator[T]) Prev() {
}
// Set implements SetIterator.
func (iter *sliceIterator[T]) Set(value T) {
func (iter *SliceIterator[T]) Set(value T) {
if iter.index == -1 {
panic("Next function should be called Set")
}
@@ -138,52 +146,60 @@ func (iter *sliceIterator[T]) Set(value T) {
iter.slice[iter.index] = value
}
func (iter *SliceIterator[T]) Reset() {
iter.index = -1
}
// FromRange creates a iterator which returns the numeric range between start inclusive and end
// exclusive by the step size. start should be less than end, step shoud be positive.
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) Iterator[T] {
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) *RangeIterator[T] {
if end < start {
panic("RangeIterator: start should be before end")
} else if step <= 0 {
panic("RangeIterator: step should be positive")
}
return &rangeIterator[T]{start: start, end: end, step: step}
return &RangeIterator[T]{start: start, end: end, step: step, current: start}
}
type rangeIterator[T constraints.Integer | constraints.Float] struct {
start, end, step T
type RangeIterator[T constraints.Integer | constraints.Float] struct {
start, end, step, current T
}
func (iter *rangeIterator[T]) HasNext() bool {
return iter.start < iter.end
func (iter *RangeIterator[T]) HasNext() bool {
return iter.current < iter.end
}
func (iter *rangeIterator[T]) Next() (T, bool) {
if iter.start >= iter.end {
func (iter *RangeIterator[T]) Next() (T, bool) {
if iter.current >= iter.end {
var zero T
return zero, false
}
num := iter.start
iter.start += iter.step
num := iter.current
iter.current += iter.step
return num, true
}
// FromRange creates a iterator which returns the numeric range between start inclusive and end
// exclusive by the step size. start should be less than end, step shoud be positive.
func FromChannel[T any](channel <-chan T) Iterator[T] {
return &channelIterator[T]{channel: channel}
func (iter *RangeIterator[T]) Reset() {
iter.current = iter.start
}
type channelIterator[T any] struct {
// FromChannel creates an iterator which returns items received from the provided channel.
// The iteration continues until the channel is closed.
func FromChannel[T any](channel <-chan T) *ChannelIterator[T] {
return &ChannelIterator[T]{channel: channel}
}
type ChannelIterator[T any] struct {
channel <-chan T
}
func (iter *channelIterator[T]) Next() (T, bool) {
func (iter *ChannelIterator[T]) Next() (T, bool) {
item, ok := <-iter.channel
return item, ok
}
func (iter *channelIterator[T]) HasNext() bool {
func (iter *ChannelIterator[T]) HasNext() bool {
return len(iter.channel) == 0
}

View File

@@ -50,15 +50,36 @@ func TestSliceIterator(t *testing.T) {
assert.Equal(false, ok)
})
// Reset
t.Run("slice iterator Reset: ", func(t *testing.T) {
iter1 := FromSlice([]int{1, 2, 3, 4})
for i := 0; i < 4; i++ {
item, ok := iter1.Next()
if !ok {
break
}
assert.Equal(i+1, item)
}
iter1.Reset()
for i := 0; i < 4; i++ {
item, ok := iter1.Next()
if !ok {
break
}
assert.Equal(i+1, item)
}
})
t.Run("slice iterator ToSlice: ", func(t *testing.T) {
iter := FromSlice([]int{1, 2, 3, 4})
item, _ := iter.Next()
assert.Equal(1, item)
data := ToSlice(iter)
data := ToSlice[int](iter)
assert.Equal([]int{2, 3, 4}, data)
})
}
func TestRangeIterator(t *testing.T) {
@@ -84,6 +105,54 @@ func TestRangeIterator(t *testing.T) {
_, ok = iter.Next()
assert.Equal(false, ok)
assert.Equal(false, iter.HasNext())
iter.Reset()
item, ok = iter.Next()
assert.Equal(1, item)
assert.Equal(true, ok)
item, ok = iter.Next()
assert.Equal(2, item)
assert.Equal(true, ok)
item, ok = iter.Next()
assert.Equal(3, item)
assert.Equal(true, ok)
_, ok = iter.Next()
assert.Equal(false, ok)
assert.Equal(false, iter.HasNext())
})
t.Run("range iterator reset: ", func(t *testing.T) {
iter := FromRange(1, 4, 1)
item, ok := iter.Next()
assert.Equal(1, item)
assert.Equal(true, ok)
item, ok = iter.Next()
assert.Equal(2, item)
assert.Equal(true, ok)
iter.Reset()
item, ok = iter.Next()
assert.Equal(1, item)
assert.Equal(true, ok)
item, ok = iter.Next()
assert.Equal(2, item)
assert.Equal(true, ok)
item, ok = iter.Next()
assert.Equal(3, item)
assert.Equal(true, ok)
_, ok = iter.Next()
assert.Equal(false, ok)
assert.Equal(false, iter.HasNext())
})
}
@@ -93,7 +162,7 @@ func TestChannelIterator(t *testing.T) {
assert := internal.NewAssert(t, "TestRangeIterator")
iter := FromSlice([]int{1, 2, 3, 4})
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
ctx, cancel := context.WithCancel(context.Background())
iter = FromChannel(ToChannel(ctx, iter, 0))

View File

@@ -21,7 +21,7 @@ func TestMapIterator(t *testing.T) {
assert := internal.NewAssert(t, "TestMapIterator")
iter := FromSlice([]int{1, 2, 3, 4})
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
iter = Map(iter, func(n int) int { return n / 2 })
@@ -34,7 +34,7 @@ func TestFilterIterator(t *testing.T) {
assert := internal.NewAssert(t, "TestFilterIterator")
iter := FromSlice([]int{1, 2, 3, 4})
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
iter = Filter(iter, func(n int) bool { return n < 3 })
@@ -47,10 +47,10 @@ func TestJoinIterator(t *testing.T) {
assert := internal.NewAssert(t, "TestJoinIterator")
iter1 := FromSlice([]int{1, 2})
iter2 := FromSlice([]int{3, 4})
var iter1 Iterator[int] = FromSlice([]int{1, 2})
var iter2 Iterator[int] = FromSlice([]int{3, 4})
iter := Join(iter1, iter2)
var iter Iterator[int] = Join(iter1, iter2)
item, ok := iter.Next()
assert.Equal(1, item)
@@ -64,7 +64,7 @@ func TestReduce(t *testing.T) {
assert := internal.NewAssert(t, "TestReduce")
iter := FromSlice([]int{1, 2, 3, 4})
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
sum := Reduce(iter, 0, func(a, b int) int { return a + b })
assert.Equal(10, sum)
}
@@ -74,7 +74,7 @@ func TestTakeIterator(t *testing.T) {
assert := internal.NewAssert(t, "TestTakeIterator")
iter := FromSlice([]int{1, 2, 3, 4, 5})
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4, 5})
iter = Take(iter, 3)

View File

@@ -5,7 +5,12 @@
package maputil
import (
"fmt"
"reflect"
"sort"
"strings"
"golang.org/x/exp/constraints"
"github.com/duke-git/lancet/v2/slice"
)
@@ -65,7 +70,12 @@ func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T
// Merge maps, next key will overwrite previous key.
// Play: https://go.dev/play/p/H95LENF1uB-
func Merge[K comparable, V any](maps ...map[K]V) map[K]V {
result := make(map[K]V, 0)
size := 0
for i := range maps {
size += len(maps[i])
}
result := make(map[K]V, size)
for _, m := range maps {
for k, v := range m {
@@ -303,3 +313,356 @@ func HasKey[K comparable, V any](m map[K]V, key K) bool {
_, haskey := m[key]
return haskey
}
// MapToStruct converts map to struct
// Play: https://go.dev/play/p/7wYyVfX38Dp
func MapToStruct(m map[string]any, structObj any) error {
for k, v := range m {
err := setStructField(structObj, k, v)
if err != nil {
return err
}
}
return nil
}
func setStructField(structObj any, fieldName string, fieldValue any) error {
structVal := reflect.ValueOf(structObj).Elem()
fName := getFieldNameByJsonTag(structObj, fieldName)
if fName == "" {
return fmt.Errorf("Struct field json tag don't match map key : %s in obj", fieldName)
}
fieldVal := structVal.FieldByName(fName)
if !fieldVal.IsValid() {
return fmt.Errorf("No such field: %s in obj", fieldName)
}
if !fieldVal.CanSet() {
return fmt.Errorf("Cannot set %s field value", fieldName)
}
val := reflect.ValueOf(fieldValue)
if fieldVal.Type() != val.Type() {
if val.CanConvert(fieldVal.Type()) {
fieldVal.Set(val.Convert(fieldVal.Type()))
return nil
}
if m, ok := fieldValue.(map[string]any); ok {
if fieldVal.Kind() == reflect.Struct {
return MapToStruct(m, fieldVal.Addr().Interface())
}
if fieldVal.Kind() == reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct {
if fieldVal.IsNil() {
fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
}
return MapToStruct(m, fieldVal.Interface())
}
}
return fmt.Errorf("Map value type don't match struct field type")
}
fieldVal.Set(val)
return nil
}
func getFieldNameByJsonTag(structObj any, jsonTag string) string {
s := reflect.TypeOf(structObj).Elem()
for i := 0; i < s.NumField(); i++ {
field := s.Field(i)
tag := field.Tag
name, _, _ := strings.Cut(tag.Get("json"), ",")
if name == jsonTag {
return field.Name
}
}
return ""
}
// ToSortedSlicesDefault converts a map to two slices sorted by key: one for the keys and another for the values.
// Play: https://go.dev/play/p/43gEM2po-qy
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V) {
keys := make([]K, 0, len(m))
// store the maps keys into a slice
for k := range m {
keys = append(keys, k)
}
// sort the slice of keys
sort.Slice(keys, func(i, j int) bool {
return keys[i] < keys[j]
})
// adjust the order of values according to the sorted keys
sortedValues := make([]V, len(keys))
for i, k := range keys {
sortedValues[i] = m[k]
}
return keys, sortedValues
}
// ToSortedSlicesWithComparator converts a map to two slices sorted by key and using a custom comparison function:
// one for the keys and another for the values.
// Play: https://go.dev/play/p/0nlPo6YLdt3
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V) {
keys := make([]K, 0, len(m))
// store the maps keys into a slice
for k := range m {
keys = append(keys, k)
}
// sort the key slice using the provided comparison function
sort.Slice(keys, func(i, j int) bool {
return comparator(keys[i], keys[j])
})
// adjust the order of values according to the sorted keys
sortedValues := make([]V, len(keys))
for i, k := range keys {
sortedValues[i] = m[k]
}
return keys, sortedValues
}
// GetOrSet returns value of the given key or set the given value value if not present.
// Play: https://go.dev/play/p/IVQwO1OkEJC
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V {
if v, ok := m[key]; ok {
return v
}
m[key] = value
return value
}
// SortByKey sorts the map by its keys and returns a new map with sorted keys.
// Play: todo
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 less(keys[i], keys[j])
})
sortedKeysMap = make(map[K]V, len(m))
for _, k := range keys {
sortedKeysMap[k] = m[k]
}
return
}
var mapHandlers = map[reflect.Kind]func(reflect.Value, reflect.Value) error{
reflect.String: convertNormal,
reflect.Int: convertNormal,
reflect.Int16: convertNormal,
reflect.Int32: convertNormal,
reflect.Int64: convertNormal,
reflect.Uint: convertNormal,
reflect.Uint16: convertNormal,
reflect.Uint32: convertNormal,
reflect.Uint64: convertNormal,
reflect.Float32: convertNormal,
reflect.Float64: convertNormal,
reflect.Uint8: convertNormal,
reflect.Int8: convertNormal,
reflect.Struct: convertNormal,
reflect.Complex64: convertNormal,
reflect.Complex128: convertNormal,
}
var _ = func() struct{} {
mapHandlers[reflect.Map] = convertMap
mapHandlers[reflect.Array] = convertSlice
mapHandlers[reflect.Slice] = convertSlice
return struct{}{}
}()
// MapTo try to map any interface to struct or base type
/*
Eg:
v := map[string]interface{}{
"service":map[string]interface{}{
"ip":"127.0.0.1",
"port":1234,
},
version:"v1.0.01"
}
type Target struct {
Service struct {
Ip string `json:"ip"`
Port int `json:"port"`
} `json:"service"`
Ver string `json:"version"`
}
var dist Target
if err := maputil.MapTo(v,&dist); err != nil {
log.Println(err)
return
}
log.Println(dist)
*/
// Play: https://go.dev/play/p/4K7KBEPgS5M
func MapTo(src any, dst any) error {
dstRef := reflect.ValueOf(dst)
if dstRef.Kind() != reflect.Ptr {
return fmt.Errorf("dst is not ptr")
}
dstElem := dstRef.Type().Elem()
if dstElem.Kind() == reflect.Struct {
srcMap := src.(map[string]interface{})
return MapToStruct(srcMap, dst)
}
dstRef = reflect.Indirect(dstRef)
srcRef := reflect.ValueOf(src)
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
srcRef = srcRef.Elem()
}
if f, ok := mapHandlers[srcRef.Kind()]; ok {
return f(srcRef, dstRef)
}
return fmt.Errorf("no implemented:%s", srcRef.Type())
}
func convertNormal(src reflect.Value, dst reflect.Value) error {
if dst.CanSet() {
if src.Type() == dst.Type() {
dst.Set(src)
} else if src.CanConvert(dst.Type()) {
dst.Set(src.Convert(dst.Type()))
} else {
return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String())
}
}
return nil
}
func convertSlice(src reflect.Value, dst reflect.Value) error {
if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice {
return fmt.Errorf("error type:%s", dst.Type().String())
}
l := src.Len()
target := reflect.MakeSlice(dst.Type(), l, l)
if dst.CanSet() {
dst.Set(target)
}
for i := 0; i < l; i++ {
srcValue := src.Index(i)
if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface {
srcValue = srcValue.Elem()
}
if f, ok := mapHandlers[srcValue.Kind()]; ok {
err := f(srcValue, dst.Index(i))
if err != nil {
return err
}
}
}
return nil
}
func convertMap(src reflect.Value, dst reflect.Value) error {
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
if src.Kind() == reflect.Interface && dst.IsValid() {
return convertMap(src.Elem(), dst)
} else {
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
}
}
dstType := dst.Type()
num := dstType.NumField()
exist := map[string]int{}
for i := 0; i < num; i++ {
k := dstType.Field(i).Tag.Get("json")
if k == "" {
k = dstType.Field(i).Name
}
if strings.Contains(k, ",") {
taglist := strings.Split(k, ",")
if taglist[0] == "" {
k = dstType.Field(i).Name
} else {
k = taglist[0]
}
}
exist[k] = i
}
keys := src.MapKeys()
for _, key := range keys {
if index, ok := exist[key.String()]; ok {
v := dst.Field(index)
if v.Kind() == reflect.Struct {
err := convertMap(src.MapIndex(key), v)
if err != nil {
return err
}
} else {
if v.CanSet() {
if v.Type() == src.MapIndex(key).Elem().Type() {
v.Set(src.MapIndex(key).Elem())
} else if src.MapIndex(key).Elem().CanConvert(v.Type()) {
v.Set(src.MapIndex(key).Elem().Convert(v.Type()))
} else if f, ok := mapHandlers[src.MapIndex(key).Elem().Kind()]; ok && f != nil {
err := f(src.MapIndex(key).Elem(), v)
if err != nil {
return err
}
} else {
return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type())
}
}
}
}
}
return nil
}
// GetOrDefault returns the value of the given key or a default value if the key is not present.
// Play: todo
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V {
if v, ok := m[key]; ok {
return v
}
return defaultValue
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"sort"
"strconv"
"time"
)
func ExampleKeys() {
@@ -450,3 +451,381 @@ func ExampleHasKey() {
// true
// false
}
func ExampleMapToStruct() {
personReqMap := map[string]any{
"name": "Nothin",
"max_age": 35,
"page": 1,
"pageSize": 10,
}
type PersonReq struct {
Name string `json:"name"`
MaxAge int `json:"max_age"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
var personReq PersonReq
_ = MapToStruct(personReqMap, &personReq)
fmt.Println(personReq)
// Output:
// {Nothin 35 1 10}
}
func ExampleToSortedSlicesDefault() {
m := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys, values := ToSortedSlicesDefault(m)
fmt.Println(keys)
fmt.Println(values)
// Output:
// [1 2 3]
// [a b c]
}
func ExampleToSortedSlicesWithComparator() {
m1 := map[time.Time]string{
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
}
keys1, values1 := ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
return a.Before(b)
})
m2 := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys2, values2 := ToSortedSlicesWithComparator(m2, func(a, b int) bool {
return a > b
})
fmt.Println(keys1)
fmt.Println(values1)
fmt.Println(keys2)
fmt.Println(values2)
// Output:
// [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]
}
func ExampleGetOrSet() {
m := map[int]string{
1: "a",
}
result1 := GetOrSet(m, 1, "1")
result2 := GetOrSet(m, 2, "b")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// a
// b
}
func ExampleSortByKey() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result := SortByKey(m, func(a, b int) bool {
return a < b
})
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
func ExampleOrderedMap_Set() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
func ExampleOrderedMap_Get() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
func ExampleOrderedMap_Front() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
frontElement, ok := om.Front()
fmt.Println(frontElement)
fmt.Println(ok)
// Output:
// {a 1}
// true
}
func ExampleOrderedMap_Back() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
backElement, ok := om.Back()
fmt.Println(backElement)
fmt.Println(ok)
// Output:
// {c 3}
// true
}
func ExampleOrderedMap_Delete() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Delete("b")
fmt.Println(om.Keys())
// Output:
// [a c]
}
func ExampleOrderedMap_Clear() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Clear()
fmt.Println(om.Keys())
// Output:
// []
}
func ExampleOrderedMap_Len() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Len()
fmt.Println(om.Len())
// Output:
// 3
}
func ExampleOrderedMap_Keys() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
keys := om.Keys()
fmt.Println(keys)
// Output:
// [a b c]
}
func ExampleOrderedMap_Values() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
values := om.Values()
fmt.Println(values)
// Output:
// [1 2 3]
}
func ExampleOrderedMap_Contains() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
result1 := om.Contains("a")
result2 := om.Contains("d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
func ExampleOrderedMap_Range() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Range(func(key string, value int) bool {
fmt.Println(key, value)
return true
})
// Output:
// a 1
// b 2
// c 3
}
func ExampleOrderedMap_Elements() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
elements := om.Elements()
fmt.Println(elements)
// Output:
// [{a 1} {b 2} {c 3}]
}
func ExampleOrderedMap_Iter() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.Iter() {
fmt.Println(elem)
}
// Output:
// {a 1}
// {b 2}
// {c 3}
}
func ExampleOrderedMap_ReverseIter() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.ReverseIter() {
fmt.Println(elem)
}
// Output:
// {c 3}
// {b 2}
// {a 1}
}
func ExampleOrderedMap_SortByKey() {
om := NewOrderedMap[int, string]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
om.SortByKey(func(a, b int) bool {
return a < b
})
fmt.Println(om.Elements())
// Output:
// [{1 a} {2 b} {3 c} {4 d}]
}
func ExampleOrderedMap_MarshalJSON() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
b, _ := om.MarshalJSON()
fmt.Println(string(b))
// Output:
// {"a":1,"b":2,"c":3}
}
func ExampleOrderedMap_UnmarshalJSON() {
// om := NewOrderedMap[string, int]()
// data := []byte(`{"a":1,"b":2,"c":3}`)
// om.UnmarshalJSON(data)
// fmt.Println(om.Elements())
}

View File

@@ -1,9 +1,11 @@
package maputil
import (
"math/cmplx"
"sort"
"strconv"
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
@@ -105,14 +107,14 @@ func TestMerge(t *testing.T) {
2: "b",
}
m2 := map[int]string{
1: "1",
3: "2",
2: "c",
3: "d",
}
expected := map[int]string{
1: "1",
2: "b",
3: "2",
1: "a",
2: "c",
3: "d",
}
acturl := Merge(m1, m2)
@@ -472,3 +474,417 @@ func TestHasKey(t *testing.T) {
assert.Equal(true, HasKey(m, "a"))
assert.Equal(false, HasKey(m, "c"))
}
func TestMapToStruct(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMapToStruct")
type (
Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Addr *Address `json:"address,omitempty"`
}
Address struct {
Street string `json:"street"`
Number int `json:"number"`
}
)
m := map[string]interface{}{
"name": "Nothin",
"age": 28,
"phone": "123456789",
"address": map[string]interface{}{
"street": "test",
"number": 1,
},
}
var p Person
err := MapToStruct(m, &p)
assert.IsNil(err)
assert.Equal(m["name"], p.Name)
assert.Equal(m["age"], p.Age)
assert.Equal(m["phone"], p.Phone)
assert.Equal("test", p.Addr.Street)
assert.Equal(1, p.Addr.Number)
}
func TestToSortedSlicesDefault(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToSortedSlicesDefault")
testCases1 := []struct {
name string
inputMap map[string]any
expKeys []string
expValues []any
}{
{
name: "Empty Map",
inputMap: map[string]any{},
expKeys: []string{},
expValues: []any{},
},
{
name: "Unsorted Map",
inputMap: map[string]any{"c": 3, "a": 1.6, "b": 2},
expKeys: []string{"a", "b", "c"},
expValues: []any{1.6, 2, 3},
},
}
for _, tc := range testCases1 {
t.Run(tc.name, func(t *testing.T) {
keys, values := ToSortedSlicesDefault(tc.inputMap)
assert.Equal(tc.expKeys, keys)
assert.Equal(tc.expValues, values)
})
}
testCases2 := map[int64]string{
7: "seven",
3: "three",
5: "five",
}
actualK2, actualV2 := ToSortedSlicesDefault(testCases2)
case2Keys := []int64{3, 5, 7}
case2Values := []string{"three", "five", "seven"}
assert.Equal(case2Keys, actualK2)
assert.Equal(case2Values, actualV2)
}
func TestToSortedSlicesWithComparator(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToSortedSlicesWithComparator")
type Account struct {
ID int
Name string
CreateTime time.Time
FavorComplexNumber complex128
Signal chan struct{}
}
type testCase struct {
name string
inputMap map[Account]any
lessFunc func(a, b Account) bool
expKeys []Account
expValues []any
}
now := time.Now()
tomorrow := now.AddDate(0, 0, 1)
yesterday := now.AddDate(0, 0, -1)
account1 := Account{ID: 1, Name: "cya", CreateTime: now, FavorComplexNumber: complex(1.2, 3),
Signal: make(chan struct{}, 10)}
account2 := Account{ID: 2, Name: "Cannian", CreateTime: tomorrow, FavorComplexNumber: complex(1.2, 2),
Signal: make(chan struct{}, 7)}
account3 := Account{ID: 3, Name: "Clauviou", CreateTime: yesterday, FavorComplexNumber: complex(3, 4),
Signal: make(chan struct{}, 3)}
account1.Signal <- struct{}{}
account2.Signal <- struct{}{}
account2.Signal <- struct{}{}
testCases := []testCase{
{
name: "Sorted by Account ID",
inputMap: map[Account]any{
account1: 100,
account2: 200,
account3: 300,
},
lessFunc: func(a, b Account) bool { return a.ID < b.ID },
expKeys: []Account{account1, account2, account3},
expValues: []any{100, 200, 300},
},
{
name: "Reverse Sorted by Account ID",
inputMap: map[Account]any{
account1: 100,
account2: 200,
account3: 300,
},
lessFunc: func(a, b Account) bool { return a.ID > b.ID },
expKeys: []Account{account3, account2, account1},
expValues: []any{300, 200, 100},
},
{
name: "Sorted by Account Name",
inputMap: map[Account]any{
account1: 100,
account2: 200,
account3: 300,
},
lessFunc: func(a, b Account) bool { return a.Name < b.Name },
expKeys: []Account{account2, account3, account1},
expValues: []any{200, 300, 100},
},
{
name: "Empty Map",
inputMap: map[Account]any{},
lessFunc: func(a, b Account) bool { return a.ID < b.ID },
expKeys: []Account{},
expValues: []any{},
},
{
name: "Sorted by Account CreateTime",
inputMap: map[Account]any{
account1: "now",
account2: "tomorrow",
account3: "yesterday",
},
lessFunc: func(a, b Account) bool { return a.CreateTime.Before(b.CreateTime) },
expKeys: []Account{account3, account1, account2},
expValues: []any{"yesterday", "now", "tomorrow"},
},
{
name: "Sorted by Account FavorComplexNumber",
inputMap: map[Account]any{
account1: "1.2+3i",
account2: "1.2+2i",
account3: "3+4i",
},
lessFunc: func(a, b Account) bool { return cmplx.Abs(a.FavorComplexNumber) < cmplx.Abs(b.FavorComplexNumber) },
expKeys: []Account{account2, account1, account3},
expValues: []any{"1.2+2i", "1.2+3i", "3+4i"},
},
{
name: "Sort by the buffer capacity of the channel",
inputMap: map[Account]any{
account1: 10,
account2: 7,
account3: 3,
},
lessFunc: func(a, b Account) bool {
return cap(a.Signal) < cap(b.Signal)
},
expKeys: []Account{account3, account2, account1},
expValues: []any{3, 7, 10},
},
{
name: "Sort by the number of elements in the channel",
inputMap: map[Account]any{
account1: 1,
account2: 2,
account3: 0,
},
lessFunc: func(a, b Account) bool { return len(a.Signal) < len(b.Signal) },
expKeys: []Account{account3, account1, account2},
expValues: []any{0, 1, 2},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
keys, values := ToSortedSlicesWithComparator(tc.inputMap, tc.lessFunc)
assert.Equal(tc.expKeys, keys)
assert.Equal(tc.expValues, values)
})
}
}
func TestGetOrSet(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGetOrSet")
m := map[int]string{
1: "a",
}
result1 := GetOrSet(m, 1, "1")
result2 := GetOrSet(m, 2, "b")
assert.Equal("a", result1)
assert.Equal("b", result2)
}
func TestSortByKey(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSortByKey")
m1 := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
expected1 := map[int]string{
1: "a",
2: "b",
3: "c",
4: "d",
}
result1 := SortByKey(m1, func(a, b int) bool {
return a < b
})
assert.Equal(expected1, result1)
m2 := map[string]int{
"c": 3,
"a": 1,
"d": 4,
"b": 2,
}
expected2 := map[string]int{
"d": 4,
"c": 3,
"b": 2,
"a": 1,
}
result2 := SortByKey(m2, func(a, b string) bool {
return a > b
})
assert.Equal(expected2, result2)
}
type (
Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Address *Address `json:"address"`
}
Address struct {
Street string `json:"street"`
Number int `json:"number"`
}
)
func TestStructType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestStructType")
src := map[string]interface{}{
"name": "Nothin",
"age": 28,
"phone": "123456789",
"address": map[string]interface{}{
"street": "test",
"number": 1,
},
}
var p Person
err := MapTo(src, &p)
assert.IsNil(err)
assert.Equal(src["name"], p.Name)
assert.Equal(src["age"], p.Age)
assert.Equal(src["phone"], p.Phone)
assert.Equal("test", p.Address.Street)
assert.Equal(1, p.Address.Number)
}
func TestBaseType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBaseType")
tc := map[string]interface{}{
"i32": -32,
"i8": -8,
"i16": -16,
"i64": -64,
"i": -1,
"u32": 32,
"u8": 8,
"u16": 16,
"u64": 64,
"u": 1,
"tf": true,
"f32": 1.32,
"f64": 1.64,
"str": "hello mapto",
"complex": 1 + 3i,
}
type BaseType struct {
I int `json:"i"`
I8 int8 `json:"i8"`
I16 int16 `json:"i16"`
I32 int32 `json:"i32"`
I64 int64 `json:"i64"`
U uint `json:"u"`
U8 uint8 `json:"u8"`
U16 uint16 `json:"u16"`
U32 uint32 `json:"u32"`
U64 uint64 `json:"u64"`
F32 float32 `json:"f32"`
F64 float64 `json:"f64"`
Tf bool `json:"tf"`
Str string `json:"str"`
Comp complex128 `json:"complex"`
}
var dist BaseType
err := MapTo(tc, &dist)
assert.IsNil(err)
var number float64
MapTo(tc["i"], &number)
assert.EqualValues(-1, number)
MapTo(tc["i8"], &number)
assert.EqualValues(-8, number)
MapTo(tc["i16"], &number)
assert.EqualValues(-16, number)
MapTo(tc["i32"], &number)
assert.EqualValues(-32, number)
MapTo(tc["i64"], &number)
assert.EqualValues(-64, number)
MapTo(tc["u"], &number)
assert.EqualValues(1, number)
MapTo(tc["u8"], &number)
assert.EqualValues(8, number)
MapTo(tc["u16"], &number)
assert.EqualValues(16, number)
MapTo(tc["u32"], &number)
assert.EqualValues(32, number)
MapTo(tc["u64"], &number)
assert.EqualValues(64, number)
}
func TestGetOrDefault(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "GetOrDefault")
m1 := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result1 := GetOrDefault(m1, 1, "123")
assert.Equal("a", result1)
result2 := GetOrDefault(m1, 5, "123")
assert.Equal("123", result2)
}

431
maputil/orderedmap.go Normal file
View File

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

245
maputil/orderedmap_test.go Normal file
View File

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

View File

@@ -1,181 +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")
}
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 {
return convertMap(src.Elem(), dst)
} else {
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
}
}
dstType := dst.Type()
num := dstType.NumField()
exist := map[string]int{}
for i := 0; i < num; i++ {
k := dstType.Field(i).Tag.Get("json")
if k == "" {
k = dstType.Field(i).Name
}
if strings.Contains(k, ",") {
taglist := strings.Split(k, ",")
if taglist[0] == "" {
k = dstType.Field(i).Name
} else {
k = taglist[0]
}
}
exist[k] = i
}
keys := src.MapKeys()
for _, key := range keys {
if index, ok := exist[key.String()]; ok {
v := dst.Field(index)
if v.Kind() == reflect.Struct {
err := convertMap(src.MapIndex(key), v)
if err != nil {
return err
}
} else {
if v.CanSet() {
if v.Type() == src.MapIndex(key).Elem().Type() {
v.Set(src.MapIndex(key).Elem())
} else if src.MapIndex(key).Elem().CanConvert(v.Type()) {
v.Set(src.MapIndex(key).Elem().Convert(v.Type()))
} else if f, ok := mapHandlers[src.MapIndex(key).Elem().Kind()]; ok && f != nil {
err := f(src.MapIndex(key).Elem(), v)
if err != nil {
return err
}
} else {
return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type())
}
}
}
}
}
return nil
}

View File

@@ -1,124 +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"`
Addr 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.Addr.Street)
assert.Equal(1, p.Addr.Number)
}
func TestBaseType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBaseType")
tc := map[string]interface{}{
"i32": -32,
"i8": -8,
"i16": -16,
"i64": -64,
"i": -1,
"u32": 32,
"u8": 8,
"u16": 16,
"u64": 64,
"u": 1,
"tf": true,
"f32": 1.32,
"f64": 1.64,
"str": "hello mapto",
"complex": 1 + 3i,
}
type BaseType struct {
I int `json:"i"`
I8 int8 `json:"i8"`
I16 int16 `json:"i16"`
I32 int32 `json:"i32"`
I64 int64 `json:"i64"`
U uint `json:"u"`
U8 uint8 `json:"u8"`
U16 uint16 `json:"u16"`
U32 uint32 `json:"u32"`
U64 uint64 `json:"u64"`
F32 float32 `json:"f32"`
F64 float64 `json:"f64"`
Tf bool `json:"tf"`
Str string `json:"str"`
Comp complex128 `json:"complex"`
}
var dist BaseType
err := MapTo(tc, &dist)
assert.IsNil(err)
var number float64
MapTo(tc["i"], &number)
assert.EqualValues(-1, number)
MapTo(tc["i8"], &number)
assert.EqualValues(-8, number)
MapTo(tc["i16"], &number)
assert.EqualValues(-16, number)
MapTo(tc["i32"], &number)
assert.EqualValues(-32, number)
MapTo(tc["i64"], &number)
assert.EqualValues(-64, number)
MapTo(tc["u"], &number)
assert.EqualValues(1, number)
MapTo(tc["u8"], &number)
assert.EqualValues(8, number)
MapTo(tc["u16"], &number)
assert.EqualValues(16, number)
MapTo(tc["u32"], &number)
assert.EqualValues(32, number)
MapTo(tc["u64"], &number)
assert.EqualValues(64, number)
}

View File

@@ -66,28 +66,28 @@ func Percent(val, total float64, n int) float64 {
return result
}
// RoundToString round up to n decimal places.
// RoundToString round off to n decimal places.
// Play: https://go.dev/play/p/kZwpBRAcllO
func RoundToString(x float64, n int) string {
func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string {
tmp := math.Pow(10.0, float64(n))
x *= tmp
x = math.Round(x)
result := strconv.FormatFloat(x/tmp, 'f', n, 64)
x *= T(tmp)
r := math.Round(float64(x))
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
return result
}
// RoundToFloat round up to n decimal places.
// RoundToFloat round off to n decimal places.
// Play: https://go.dev/play/p/ghyb528JRJL
func RoundToFloat(x float64, n int) float64 {
func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
tmp := math.Pow(10.0, float64(n))
x *= tmp
x = math.Round(x)
return x / tmp
x *= T(tmp)
r := math.Round(float64(x))
return r / tmp
}
// TruncRound round off n decimal places.
// Play: https://go.dev/play/p/aumarSHIGzP
func TruncRound(x float64, n int) float64 {
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T {
floatStr := fmt.Sprintf("%."+strconv.Itoa(n+1)+"f", x)
temp := strings.Split(floatStr, ".")
var newFloat string
@@ -97,6 +97,44 @@ func TruncRound(x float64, n int) float64 {
newFloat = temp[0] + "." + temp[1][:n]
}
result, _ := strconv.ParseFloat(newFloat, 64)
return T(result)
}
// FloorToFloat round down to n decimal places.
// Play: https://go.dev/play/p/vbCBrQHZEED
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
tmp := math.Pow(10.0, float64(n))
x *= T(tmp)
r := math.Floor(float64(x))
return r / tmp
}
// FloorToString round down to n decimal places.
// Play: https://go.dev/play/p/Qk9KPd2IdDb
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string {
tmp := math.Pow(10.0, float64(n))
x *= T(tmp)
r := math.Floor(float64(x))
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
return result
}
// CeilToFloat round up to n decimal places.
// Play: https://go.dev/play/p/8hOeSADZPCo
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
tmp := math.Pow(10.0, float64(n))
x *= T(tmp)
r := math.Ceil(float64(x))
return r / tmp
}
// CeilToString round up to n decimal places.
// Play: https://go.dev/play/p/wy5bYEyUKKG
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string {
tmp := math.Pow(10.0, float64(n))
x *= T(tmp)
r := math.Ceil(float64(x))
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
return result
}
@@ -253,7 +291,7 @@ func PointDistance(x1, y1, x2, y2 float64) float64 {
return math.Sqrt(c)
}
// IsPrimes checks if number is prime number.
// IsPrime checks if number is prime number.
// Play: https://go.dev/play/p/Rdd8UTHZJ7u
func IsPrime(n int) bool {
if n < 2 {
@@ -351,3 +389,9 @@ func Abs[T constraints.Integer | constraints.Float](x T) T {
return x
}
// Div returns the result of x divided by y.
// Play: https://go.dev/play/p/WLxDdGXXYat
func Div[T constraints.Float | constraints.Integer](x T, y T) float64 {
return float64(x) / float64(y)
}

View File

@@ -110,6 +110,66 @@ func ExampleTruncRound() {
// 0.125
}
func ExampleCeilToFloat() {
result1 := CeilToFloat(3.14159, 1)
result2 := CeilToFloat(3.14159, 2)
result3 := CeilToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5
}
func ExampleCeilToString() {
result1 := CeilToString(3.14159, 1)
result2 := CeilToString(3.14159, 2)
result3 := CeilToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5.0000
}
func ExampleFloorToFloat() {
result1 := FloorToFloat(3.14159, 1)
result2 := FloorToFloat(3.14159, 2)
result3 := FloorToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5
}
func ExampleFloorToString() {
result1 := FloorToString(3.14159, 1)
result2 := FloorToString(3.14159, 2)
result3 := FloorToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5.0000
}
func ExampleAverage() {
result1 := Average(1, 2)
result2 := RoundToFloat(Average(1.2, 1.4), 1)
@@ -404,3 +464,17 @@ func ExampleAbs() {
// 0.1
// 0.2
}
func ExampleDiv() {
result1 := Div(9, 4)
result2 := Div(1, 2)
result3 := Div(0, 666)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 2.25
// 0.5
// 0
}

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