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

Compare commits

...

960 Commits

Author SHA1 Message Date
dudaodong
bdfdeaf496 merge branch 'main' into v2 2023-09-11 11:40:46 +08:00
dudaodong
172c44c07a doc: update doc site 2023-09-11 11:39:21 +08:00
dudaodong
534263eb08 release v2.2.6 2023-09-11 11:38:45 +08:00
dudaodong
a25b1ac7e3 doc: update doc site 2023-09-11 11:35:37 +08:00
dudaodong
d84f9777ea doc: update doc site 2023-09-11 11:33:39 +08:00
godaner
f9caaf8063 Update strutil.md (#130)
Update Coverts string to upper KEBAB-CASE,... to Coverts string to upper snake_case,...
2023-09-08 20:14:10 +08:00
dudaodong
f198711d1c doc: add doc for new functions 2023-09-07 16:03:08 +08:00
dudaodong
19378ca4d1 feat: add IsVisa, IsMasterCard, IsAmericanExpress, IsUnionPay, IsChinaUnionPay 2023-09-06 18:05:15 +08:00
dudaodong
71c7733eb0 feat: add IsBin, IsHex, IsBase64URL, IsJWT 2023-09-06 17:42:31 +08:00
dudaodong
20786c360b test: add cases for TestIsIpV4 and TestIsIpV6 2023-09-06 16:27:14 +08:00
dudaodong
51fafa110e merge main 2023-09-06 16:19:11 +08:00
Wang Huan
07fc453b74 fix the bug in cloning slices (#129)
* test: add test cases for cloning slices.

* fix: fix the bug in cloning slices.
2023-09-06 16:16:25 +08:00
dudaodong
b309044981 doc: add doc for Partition 2023-09-04 16:23:23 +08:00
dudaodong
541e6d4ea3 feat: add Partition for slice 2023-09-04 11:30:50 +08:00
dudaodong
4037b96cc4 refactor: update SortByField 2023-09-04 11:03:22 +08:00
dudaodong
8e484c4a6f doc: update doc site 2023-09-04 10:31:02 +08:00
dudaodong
dd339563bc doc: add contribution and contributors to doc site 2023-09-01 16:47:33 +08:00
dudaodong
9567dcc57f doc: update doc site 2023-09-01 16:01:17 +08:00
dudaodong
de104ebeb6 doc: update contributors setting 2023-08-31 17:07:31 +08:00
dudaodong
eebd95395f doc: add contributors setting 2023-08-31 16:59:38 +08:00
dudaodong
56265647ff doc: update doc styles 2023-08-31 16:42:33 +08:00
dudaodong
756da3719d doc: update doc styles 2023-08-31 15:45:47 +08:00
dudaodong
f3008ac7c4 doc: update sponsor part 2023-08-31 15:11:02 +08:00
dudaodong
c6583d3b83 doc: update sponsor part 2023-08-31 15:03:36 +08:00
dudaodong
a3d7bac72e doc: add sponsor part 2023-08-31 14:56:40 +08:00
dudaodong
74b4d53514 doc: add sponsor doc 2023-08-31 14:45:40 +08:00
dudaodong
a08afe0d77 Merge branch 'main' into v2 2023-08-31 11:17:06 +08:00
DuDaoDong
ee680aa84c Create FUNDING.yml 2023-08-31 11:15:49 +08:00
dudaodong
caa74064f0 doc: update CONTRIBUTING doc 2023-08-31 10:06:12 +08:00
dudaodong
abc94c0b26 doc: remove dist 2023-08-30 20:01:23 +08:00
dudaodong
86bcdce07b doc: remove dist 2023-08-30 20:01:09 +08:00
dudaodong
789f5d933d doc: update readme file 2023-08-30 17:52:41 +08:00
dudaodong
156fd2ef5d doc: build doc 2023-08-30 15:08:27 +08:00
dudaodong
ea036b3a81 doc: add space size for build doc 2023-08-30 14:52:38 +08:00
dudaodong
3daca8b331 doc: update validator doc 2023-08-30 14:25:12 +08:00
dudaodong
21a952d2be doc: update doc styles 2023-08-30 11:59:04 +08:00
dudaodong
03d331dfb6 doc: update doc for website 2023-08-30 11:46:16 +08:00
dudaodong
ef79e1305e doc: add links to site 2023-08-30 10:37:51 +08:00
dudaodong
7c2670d019 doc: add Contributing Guide 2023-08-29 18:01:57 +08:00
dudaodong
31f64c0a98 doc: update doc for website 2023-08-29 17:20:54 +08:00
dudaodong
28b8986cbe doc: update doc styles 2023-08-29 15:20:04 +08:00
dudaodong
b4ff33ce80 doc: update doc site 2023-08-29 15:12:32 +08:00
dudaodong
25fa117712 doc: update doc site 2023-08-29 14:09:29 +08:00
dudaodong
0c9f539e3d doc: add dist folder 2023-08-29 11:49:29 +08:00
dudaodong
05bd1afaa6 doc: build doc 2023-08-29 11:47:43 +08:00
dudaodong
51cb665110 doc: fix tag error 2023-08-29 11:40:40 +08:00
dudaodong
47c368e8f2 doc: update doc for slice retry random package 2023-08-28 16:41:36 +08:00
dudaodong
3127360b28 doc: update doc for slice retry random package 2023-08-28 16:41:14 +08:00
dudaodong
5b6def5ff0 doc: update style link 2023-08-27 11:50:44 +08:00
dudaodong
33d99ad7d4 doc: add demo 2023-08-27 11:43:48 +08:00
dudaodong
8bd31d39c3 docs: update doc 2023-08-27 11:41:29 +08:00
dudaodong
a418549525 doc: update meta url 2023-08-25 17:43:10 +08:00
dudaodong
1f849efe1c doc: update doc for datetime fileutil formatter function package 2023-08-25 14:31:49 +08:00
dudaodong
8ad374bb21 doc: update doc for datastructure package 2023-08-25 11:05:33 +08:00
dudaodong
a69d886565 doc: add go playground demo 2023-08-25 10:49:16 +08:00
dudaodong
cf58542b4a doc: add new doc for convertor and cryptor 2023-08-25 10:47:16 +08:00
dudaodong
51f166d1d9 doc: update doc styles 2023-08-24 19:27:47 +08:00
dudaodong
fbeb031b40 doc: update doc 2023-08-24 19:07:24 +08:00
dudaodong
095cfc0aab doc: add new site for doc 2023-08-24 17:47:03 +08:00
ggymm
e66ab154bc fix: 覆盖写入字符串到文件问题 (#128) 2023-08-24 16:36:44 +08:00
dudaodong
91bc1a512c test: update TestZipFolder 2023-08-22 10:49:46 +08:00
dudaodong
8753255026 doc: update link in readme 2023-08-22 10:43:52 +08:00
dudaodong
9cf1f13fec doc: update link in readme 2023-08-22 10:39:37 +08:00
dudaodong
5d78bae4bb doc: update link in readme 2023-08-22 10:34:16 +08:00
dudaodong
27777eecc0 doc: update copyonwritelist doc 2023-08-22 10:26:39 +08:00
dudaodong
1a7e0e8792 doc: add copyonwritelist 2023-08-22 10:25:21 +08:00
dudaodong
02b7aa8f33 update version 2023-08-22 10:18:21 +08:00
dudaodong
f188d3d08f doc: add doc for InDelta 2023-08-21 11:05:11 +08:00
dudaodong
0c924b859e Merge branch 'main' into v2 2023-08-21 11:00:23 +08:00
warpmatrix
c5a5a07462 feat: InDelta (#127) 2023-08-15 09:53:16 +08:00
dudaodong
1ff5dd6df0 Merge branch 'main' into v2 2023-08-03 15:06:15 +08:00
Faucherwind
b5f86a488c feat: CopyOnWriteList adds more functions such as ForEach, Sort... (#126)
* feat: CopyOnWriteList adds more functions such as ForEach, Sort

* feat: CopyOnWriteList adds more functions such as ForEach, Sort
2023-08-03 15:05:30 +08:00
dudaodong
1390b7a964 fix: fix bug of Zip 2023-08-03 10:45:48 +08:00
dudaodong
c3e28a9fc0 fix: fix bug of Zip 2023-08-02 20:17:55 +08:00
dudaodong
e924429d6e fix: fix test case TestConcurrentMap_GetAndDelete 2023-08-02 11:33:27 +08:00
dudaodong
7079b1f704 format doc 2023-08-02 11:29:51 +08:00
Faucherwind
40cad365c0 feat: add CopyOnWriteList. A thread-safe list. (#125)
* fix: use loop to get value . On Get() and Contains()

* fix: Use reflect. DeepEqual() determines whether the keys are equal

* feat: add CopyOnWriteList. A thread-safe list.

* fix: fix failed unit test

* feat: add Equal() in CopyOnWriteList.

fix: update some function`s  names

* doc: add copyonwritelist.md and copyonwritelist_zh-CN.md
2023-08-02 11:10:10 +08:00
Faucherwind
6386ab908d fix: use loop to get value . On Get() and Contains() (#124)
* fix: use loop to get value . On Get() and Contains()

* fix: Use reflect. DeepEqual() determines whether the keys are equal
2023-08-01 11:14:45 +08:00
dudaodong
bb563724c7 doc: add go playground demo 2023-08-01 11:01:57 +08:00
dudaodong
72924d4486 release v2.2.4 2023-08-01 09:58:53 +08:00
dudaodong
231f8b04b4 doc: add new function doc for v2.2.4 2023-07-30 13:22:32 +08:00
dudaodong
daa932fee3 update: make IsWeekend deprecated 2023-07-30 12:47:00 +08:00
dudaodong
1b0691f1d5 Merge branch 'main' into v2 2023-07-30 12:41:03 +08:00
CyJaySong
0b6a00bd99 IsWeekend func Deprecated (#123)
* expand BeginOfWeek、EndOfWeek

* IsWeekend func Deprecated
2023-07-30 12:39:26 +08:00
dudaodong
4ab98664bb refactor: rename file name 2023-07-28 17:55:36 +08:00
dudaodong
31eb5f4d1f feat: add HmacMd5WithBase64 2023-07-28 17:53:46 +08:00
dudaodong
0ae5d17a06 doc: add doc for CurrentMap 2023-07-27 15:08:55 +08:00
dudaodong
4558d7a3c2 test: add test for currentmap 2023-07-27 11:54:52 +08:00
dudaodong
4715301240 doc: update doc for cryptor package 2023-07-27 11:20:06 +08:00
dudaodong
1c58ee23f1 feat: add Timestamp,TimestampMilli,TimestampMicro, TimestampNano 2023-07-26 17:28:15 +08:00
dudaodong
2a303d5e4b doc: update format 2023-07-26 15:28:20 +08:00
dudaodong
326e7881a6 feat: add NowDateOrTime 2023-07-26 15:03:59 +08:00
dudaodong
31e8b12674 feat: add timezone support for FormatTimeToStr and FormatStrToTime 2023-07-26 14:40:12 +08:00
dudaodong
a0431d9435 fix: fix TestConcurrentMap_Delete test failed 2023-07-25 10:21:11 +08:00
dudaodong
0456b65cc7 test: add example for cryptor package new functions 2023-07-25 10:18:54 +08:00
dudaodong
989b4dd791 test: add test for cryptor package new functions 2023-07-25 10:06:25 +08:00
dudaodong
a76b02fbba Merge branch 'main' into v2 2023-07-24 17:28:41 +08:00
hoslo
2d7747738a feat: add base cryptor method (#119)
Co-authored-by: shuai_yang <shuai_yang@intsig.net>
2023-07-24 17:24:17 +08:00
dudaodong
fe0cb04137 feat: add ConcurrentMap 2023-07-24 17:10:45 +08:00
dudaodong
fe0264f628 refactor: clean code 2023-07-24 15:42:36 +08:00
dudaodong
65396cee32 Merge branch 'main' into v2 2023-07-23 15:43:35 +08:00
Ta
544702b460 fix: remove unused message in pointer.md (#117) 2023-07-23 15:42:54 +08:00
dudaodong
d8936cdcb5 doc: add doc for pointer 2023-07-23 13:26:07 +08:00
dudaodong
d4b97d6b20 Merge branch 'main' into v2 2023-07-23 13:13:56 +08:00
Ta
5caa14c838 feat: add UnwarpOr and UnwarpOrDefault to pointer pkg (#116) 2023-07-23 13:09:50 +08:00
dudaodong
0d0848ac67 refactor: use for loop than for range 2023-07-10 10:56:53 +08:00
dudaodong
d5334f892f feat: add Join 2023-07-10 10:51:43 +08:00
dudaodong
36ef5b3bd3 doc: add doc for HasKey 2023-07-10 10:39:55 +08:00
dudaodong
6f48b00c88 feat: add HasKey 2023-07-10 10:33:04 +08:00
dudaodong
7b744c299e fix: fix bug of CreateDir 2023-07-10 10:13:23 +08:00
dudaodong
154ec56780 doc: add go playground demo 2023-07-04 16:31:13 +08:00
dudaodong
584aabdf62 release v2.2.3 2023-07-03 14:09:04 +08:00
dudaodong
1b754a6264 doc: update link in readme 2023-07-03 11:57:50 +08:00
dudaodong
06a558d797 doc: update link in readme 2023-07-03 11:21:49 +08:00
dudaodong
423779d3ff doc: update link in readme 2023-07-03 11:14:55 +08:00
dudaodong
78c17a9e7b doc: update link in readme 2023-07-03 10:52:26 +08:00
dudaodong
529b9317d0 doc: update link in readme 2023-07-03 10:48:04 +08:00
dudaodong
dc838f645b doc: update link in readme 2023-07-03 10:43:05 +08:00
dudaodong
7f559c4940 doc: update link in readme 2023-07-03 10:42:22 +08:00
dudaodong
acb08dfd90 doc: update link in readme 2023-07-03 10:40:50 +08:00
dudaodong
ffe46fd06d update readme file 2023-07-02 22:31:17 +08:00
dudaodong
b419e46b9b doc: add package index 2023-07-02 22:12:47 +08:00
dudaodong
8fbcdfd515 doc: add pointer and tuple package to readme file 2023-07-02 21:06:10 +08:00
dudaodong
f03f48210c doc: update system package 2023-06-30 15:12:18 +08:00
dudaodong
742944e2f0 doc: add doc for tuple package 2023-06-30 15:11:26 +08:00
dudaodong
c0b491ad78 doc: add example for tuple package 2023-06-30 11:20:46 +08:00
dudaodong
e9abae2a92 doc: add doc for pointer package 2023-06-30 10:31:22 +08:00
dudaodong
8229de2f10 test: add parallel running for all unit test functions 2023-06-30 10:18:45 +08:00
dudaodong
ab364744b6 test: add parallel running for all unit test functions 2023-06-29 14:49:28 +08:00
dudaodong
17ff84fa1f test: add test for Tuple Zip and Unzip 2023-06-29 11:34:43 +08:00
dudaodong
bf50baa07d feat: add GetTodayStartTime and GetTodayEndTime 2023-06-29 11:03:28 +08:00
dudaodong
7d56da8108 feat: add Md5Byte 2023-06-29 10:33:29 +08:00
dudaodong
16c2df711b feat: add unzip for tuple 2023-06-28 20:06:02 +08:00
dudaodong
015f8c3f5c feat: add zip 2023-06-28 19:41:46 +08:00
dudaodong
ba25701d89 feat: add zip 2023-06-28 17:52:12 +08:00
dudaodong
a0cb4bb266 doc: add example for tuple package 2023-06-28 16:25:43 +08:00
dudaodong
d6ba7497f9 feat: add Tuple package 2023-06-28 15:58:41 +08:00
dudaodong
7da931e0a0 feat: add Abs 2023-06-27 10:24:35 +08:00
dudaodong
8f49078eb3 doc: add go playground demo 2023-06-20 14:04:18 +08:00
dudaodong
3050d93703 release v2.2.2 2023-06-20 11:11:27 +08:00
dudaodong
efcfbfb6a1 fix: fix failed unit test 2023-06-20 11:10:33 +08:00
dudaodong
844b7a2c3b release v2.2.2 2023-06-20 10:59:12 +08:00
dudaodong
c8a536eafc doc: add doc for ZipAppendEntry 2023-06-17 19:54:09 +08:00
dudaodong
0fd268face Merge branch 'main' into v2 2023-06-17 19:38:53 +08:00
TheLastSunset
8ced7e887f feat: add ZipAppendEntry (#113) 2023-06-17 19:36:42 +08:00
dudaodong
957568ed5c doc: format code in doc file 2023-06-15 10:13:27 +08:00
dudaodong
fffabd0ffa Merge branch 'main' into v2 2023-06-13 13:58:29 +08:00
Mickls
e839af3ef9 feat: Add support for uploading files in SendRequest (#111) 2023-06-13 13:55:47 +08:00
dudaodong
1650e70d04 Merge branch 'main' into v2 2023-06-13 10:15:17 +08:00
Thijs Schreijer
23382b2b76 fix doc comment typo (#110) 2023-06-12 20:13:14 +08:00
dudaodong
daa3aa3da2 doc: update text error 2023-06-09 11:31:16 +08:00
dudaodong
be355a23f3 doc: add doc for Utf8ToGbk and GbkToUtf8 2023-06-09 11:29:29 +08:00
dudaodong
95af83b0cb doc: add doc for Sum 2023-06-09 11:23:24 +08:00
dudaodong
1efdbc0973 doc: add doc for RemoveWhiteSpace 2023-06-09 11:19:35 +08:00
dudaodong
9b829aa695 feat: add RemoveWhiteSpace 2023-06-08 16:47:31 +08:00
dudaodong
bfa9091c09 test: clean code 2023-06-08 16:25:48 +08:00
dudaodong
c11d63c2e2 feat: add Sum 2023-06-08 16:18:25 +08:00
dudaodong
83832daeb1 feat: add Utf8ToGbk and GbkToUtf8 2023-06-08 15:50:44 +08:00
dudaodong
7311f84772 test: add test file 2023-06-08 14:50:46 +08:00
dudaodong
a63d78111e test: update ExampleRandUniqueIntSlice 2023-06-08 14:41:17 +08:00
dudaodong
e2fc2f1bc9 feat: add WirteCsvFile 2023-06-08 14:40:37 +08:00
dudaodong
23a0135947 doc: add doc for RandUniqueIntSlice 2023-06-07 14:21:29 +08:00
dudaodong
013b6457bb Merge branch 'main' into v2 2023-06-07 11:38:20 +08:00
dudaodong
e08a62b0ba doc: add doc for Log function 2023-06-07 11:37:12 +08:00
Liu Shuang
850800a233 feat: add RandUniqueIntSlice (#108) 2023-06-07 11:33:37 +08:00
dudaodong
a6a8fd88bc feat: add Log function 2023-06-06 14:34:40 +08:00
dudaodong
2f6ee84443 feat: add Of and Unwrap 2023-06-02 11:48:17 +08:00
dudaodong
b1c6614549 refactor: clean code 2023-06-01 16:58:42 +08:00
dudaodong
a8761eefb0 doc: update package comment for datastructure package 2023-06-01 14:28:04 +08:00
dudaodong
cbf8cfdffa doc: add go playground demo 2023-06-01 14:11:57 +08:00
dudaodong
286a187942 fix: fix go report validation issue 2023-06-01 11:49:49 +08:00
dudaodong
b787e99528 release v2.2.1 2023-06-01 10:45:32 +08:00
dudaodong
e54c9b2850 doc: add new function to readme file 2023-06-01 10:44:00 +08:00
dudaodong
8944109c4c doc: add doc for WriteBytesToFile and WriteStringToFile 2023-05-31 17:42:34 +08:00
dudaodong
10e3732f32 feat: add IsWeekend 2023-05-31 17:13:06 +08:00
dudaodong
a415597c6b Merge branch 'main' into v2 2023-05-31 17:06:47 +08:00
hhhhhxm
69b32fd043 新增判断是否是周末的方法 (#105)
* 新增判断是否是周末的方法

* 新增判断是否是周末的方法

---------

Co-authored-by: huangxingming <huangxingming@kezaihui.com>
2023-05-31 17:05:20 +08:00
dudaodong
75ed359084 feat: add WriteBytesToFile and WriteStringToFile 2023-05-31 16:56:18 +08:00
dudaodong
2c71b6375c doc: add doc for ContainsAny and ContainsAll 2023-05-31 15:56:20 +08:00
dudaodong
2894bec80c feat: add ContainsAll and ContainsAny 2023-05-31 10:51:16 +08:00
dudaodong
09ec5b97a6 doc: add doc for DayOfYear 2023-05-31 10:04:59 +08:00
hhhhhxm
46ecb117a5 增加判断某个日期是一年当中的第几天的方法 (#103)
* 增加判断某个日期是一年当中的第几天的方法

* 修改下函数名字

---------

Co-authored-by: huangxingming <huangxingming@kezaihui.com>
2023-05-31 09:54:23 +08:00
dudaodong
388171e739 doc: add doc for BetweenSeconds 2023-05-30 17:47:16 +08:00
dudaodong
6fbaf2b005 refactor: update TestBetweenSeconds and ExampleBetweenSeconds 2023-05-30 17:44:28 +08:00
dudaodong
53a91cad71 Merge branch 'main' into v2 2023-05-30 17:38:58 +08:00
tlei995
a51a182fb2 feat:add betweenSeconds func (#102)
Co-authored-by: leitao <leitao@kezaihui.com>
2023-05-30 17:37:29 +08:00
dudaodong
64982f0c89 fix: fix body param bug when send post request 2023-05-30 10:51:33 +08:00
dudaodong
f38f69ce17 Merge branch 'main' into v2 2023-05-29 20:10:34 +08:00
燕归来
a33ea3d013 fix: timeFormat["yyyy-mm-dd hh"] should be "2006-01-02 15" (#99) (#100)
* fix: timeFormat["yyyy-mm-dd hh"] should be "2006-01-02 15" (#99)

* test: add use cases for FormatTimeToStr
2023-05-29 20:08:17 +08:00
dudaodong
e35462fb14 fix: fix format error in init function 2023-05-29 16:09:30 +08:00
dudaodong
af106a4a8e doc: add doc for Cos and Sin 2023-05-28 20:12:40 +08:00
dudaodong
ec7232ec40 update readme file 2023-05-25 19:29:59 +08:00
dudaodong
67c1b54b5a feat: add Cos and Sin function 2023-05-25 17:51:40 +08:00
dudaodong
e149ae2f72 feat: add HideString 2023-05-23 18:39:59 +08:00
dudaodong
b1fcfce188 doc: add go playground demo 2023-05-19 11:42:29 +08:00
dudaodong
259dbce85e doc: add go playground demo 2023-05-19 11:42:17 +08:00
will
78aa679670 feat: add ContainNumber for validator (#97)
Co-authored-by: sunyaoyao <sunyaoyao@kezaihui.com>
2023-05-19 11:23:19 +08:00
dudaodong
3beb769f09 release v2.2.0 2023-05-18 10:53:02 +08:00
dudaodong
8af02ff95b doc: update readme file 2023-05-18 10:51:33 +08:00
dudaodong
ba4485d8c0 merge main 2023-05-18 10:32:36 +08:00
dudaodong
8a1dd40738 doc: add doc for AddYear function 2023-05-18 10:27:37 +08:00
dudaodong
85e2806531 doc: add doc for FindLast 2023-05-18 10:21:24 +08:00
will
1616d3d1be feat:addFindLast for stream (#95)
Co-authored-by: sunyaoyao <sunyaoyao@kezaihui.com>
2023-05-18 10:17:15 +08:00
tlei995
aa8e0d5c12 feat:add AddYear func (#96)
Co-authored-by: leitao <leitao@kezaihui.com>
2023-05-18 10:14:23 +08:00
dudaodong
f05477d9cf fix: update logic of Percent function 2023-05-18 10:11:48 +08:00
dudaodong
cd91e16b26 doc: update document for strutil and convertor package 2023-05-11 10:06:32 +08:00
dudaodong
9fcf046fb3 feat: add Trim and SplitAndTrim 2023-05-10 10:53:17 +08:00
dudaodong
c3f1bc39d7 feat: add ReplaceWithMap 2023-05-10 10:29:26 +08:00
dudaodong
f5784b0f46 feat: add ReplaceByMap 2023-05-10 10:03:13 +08:00
dudaodong
c86a8a479d feat: add ToInterface 2023-05-09 12:01:57 +08:00
dudaodong
e2aeb8ec07 doc: add document for GCD and LCM function 2023-05-06 11:28:38 +08:00
dudaodong
654ba15aaf feat: add GCD and LCM function 2023-05-06 11:02:00 +08:00
dudaodong
945c59896b feat: add IsLeapYear 2023-04-28 14:42:25 +08:00
dudaodong
219e31d929 fix: fix format issue 2023-04-27 15:49:58 +08:00
dudaodong
82cb86b35c doc: add go playground demo for compare package 2023-04-27 14:15:25 +08:00
dudaodong
f93c561f5d doc: update go playground demo 2023-04-27 12:03:15 +08:00
dudaodong
4888909208 fix: fix IsPingConnected failed in windows 2023-04-26 19:53:53 +08:00
dudaodong
33c8875d14 test: update TestWordCount 2023-04-26 19:35:28 +08:00
dudaodong
99cb1b13a3 test: remove unstable test item 2023-04-26 18:14:27 +08:00
dudaodong
42ec189995 release v2.1.20 2023-04-26 18:03:52 +08:00
dudaodong
424c291813 test: add TestExecCommandWithOption 2023-04-26 17:53:20 +08:00
dudaodong
4311b9ac66 test: remove t.Log() 2023-04-26 14:59:45 +08:00
dudaodong
581e338889 doc: add new function to readme file 2023-04-26 14:31:47 +08:00
dudaodong
f399425c2a doc: add document for compare package 2023-04-26 11:02:11 +08:00
dudaodong
dcdb29334d doc: update fileutils package document 2023-04-26 10:44:12 +08:00
dudaodong
4859f3ca23 test: add examples for compare package 2023-04-26 10:19:42 +08:00
dudaodong
9f2528842e feat: add unit test for compare package 2023-04-26 10:05:09 +08:00
dudaodong
6066d6669f feat: add compare package 2023-04-25 18:01:05 +08:00
dudaodong
e3804e9534 test: add test.csv file 2023-04-25 11:34:09 +08:00
dudaodong
2f51397d2c feat: add ReadCsvFile 2023-04-25 11:28:43 +08:00
dudaodong
11217a11c7 feat: add FileSize, MTime, Sha 2023-04-25 11:16:00 +08:00
dudaodong
fa81ee143e refactor: update param name CopyFile 2023-04-25 10:49:12 +08:00
dudaodong
62891f20f8 feat: add IsZipFile 2023-04-24 14:00:53 +08:00
dudaodong
5e79eaa9dd feat: add IsZipFile 2023-04-24 11:00:58 +08:00
dudaodong
aaf45012e4 fix: refact CopyProperties 2023-04-22 14:14:13 +08:00
dudaodong
48c17ea1ce fix: update params in TestIsPingConnected 2023-04-19 17:40:55 +08:00
dudaodong
e90283c3f9 fix: update params in TestIsPingConnected 2023-04-19 17:35:17 +08:00
dudaodong
f82c4a305d fix: update params in TestIsPingConnected 2023-04-19 17:32:30 +08:00
dudaodong
05997603a9 doc: add document for DownloadFile and UploadFile 2023-04-19 16:16:47 +08:00
dudaodong
6d5ec807f7 test: comment some test case 2023-04-19 16:04:35 +08:00
dudaodong
f73c7e7e86 doc: add document and example for IsPingConnected and IsTelnetConnected 2023-04-19 11:48:28 +08:00
dudaodong
c137428b9e feat: add IsPingConnected and IsTelnetConnected 2023-04-19 11:14:10 +08:00
dudaodong
9f68620b37 feat: add IsPingConnected and IsTelnetConnected 2023-04-19 11:13:21 +08:00
dudaodong
2cdbba56a4 feat: add UploadFile and DownloadFile 2023-04-18 20:09:56 +08:00
dudaodong
01a3b139c0 doc: add document for new functions in slice and strutil package 2023-04-18 15:49:09 +08:00
dudaodong
fcb3b97b45 doc: add docment and playground demo for v2.1.19 2023-04-18 15:21:03 +08:00
dudaodong
52ea64bc33 feat: add FindLastBy function 2023-04-17 20:17:26 +08:00
燕归来
14bc08c6d6 feat: add FindBy that a result will be without unrefrence #88 (#90)
feat: add example for FindBy

chore: reduce code duplication
2023-04-17 20:11:56 +08:00
dudaodong
47bdd6718a fix: ExecCommand can't compile in macos 2023-04-17 17:03:46 +08:00
dudaodong
18d27604e6 update mod file 2023-04-17 17:00:53 +08:00
dudaodong
66bd339e3a doc: add example for strutil new functions 2023-04-17 16:35:26 +08:00
燕归来
04abb7a3ea feat: add some function for strutil package #88 (#89) 2023-04-17 16:07:14 +08:00
dudaodong
975c303a31 fix: ExampleListFileNames 2023-04-17 15:35:17 +08:00
dudaodong
3b8dd94a5c fix: TestListFileNames 2023-04-17 15:32:59 +08:00
dudaodong
815791c0b6 Merge branch 'main' into v2 2023-04-17 15:28:37 +08:00
dudaodong
a12a691ee6 release v2.1.19 2023-04-17 15:28:01 +08:00
Nothin
247cf89947 [FEATURE] system add option (#87) 2023-04-17 14:06:49 +08:00
dudaodong
c3fad62d8c doc: update stream package document 2023-04-17 13:53:47 +08:00
dudaodong
a8a96be21b doc: update doc for new added functions 2023-04-17 11:34:17 +08:00
dudaodong
8e297769b2 fix: fix example comment of MapTo function 2023-04-17 11:06:03 +08:00
dudaodong
98cdf1c040 Merge branch 'main' into v2 2023-04-17 10:38:28 +08:00
dudaodong
d7976e31a4 refactor: move typemap.go to maputil package and add document for it 2023-04-17 10:37:56 +08:00
dudaodong
5b11a8b457 refactor: move typemap.go to maputil package and add document for it 2023-04-17 10:36:59 +08:00
Nothin
d4a16534f2 add typemap (#85)
* [FEATURE] typemap, quick map any type to specified type

* [DOC] add more test case

---------

Co-authored-by: zhijian.chen <zhijian.chen@longsys.com>
2023-04-17 10:04:31 +08:00
dudaodong
ecf0688788 update readme file 2023-04-07 17:39:54 +08:00
dudaodong
90f9cad1ea update readme file 2023-04-07 15:23:34 +08:00
dudaodong
4a298876e9 doc: add docment and example for byte.go 2023-04-07 14:53:48 +08:00
dudaodong
74474cd9ef feat: add feature for humanize byte unit 2023-04-07 14:18:28 +08:00
dudaodong
f23f18457e feat: add Pretty and PrettyToWriter 2023-04-06 16:30:38 +08:00
dudaodong
18f01ffd75 doc: update doc and example for validator 2023-04-06 10:46:31 +08:00
dudaodong
c53d541a6b doc: add document for stream package 2023-04-05 20:17:41 +08:00
dudaodong
5b9b4c4344 test: add example for stream package 2023-04-05 18:52:27 +08:00
dudaodong
6e7300bbbf feat: add CurrentPath 2023-04-05 15:55:55 +08:00
dudaodong
3685aee02b fix: fix bug of Comma function 2023-04-05 14:26:42 +08:00
dudaodong
046f3e0bf9 feat: add IsInt, IsFloat, IsNumber 2023-04-05 14:07:23 +08:00
dudaodong
c01c9d14b4 feat: add ReduceRight 2023-04-04 17:54:54 +08:00
dudaodong
f198191063 feat: add ReduceBy 2023-04-04 17:41:37 +08:00
dudaodong
8bdd46bda4 doc: add play ground demo 2023-04-03 10:33:57 +08:00
dudaodong
e29b56c3c3 release v2.1.18 2023-04-03 10:12:50 +08:00
dudaodong
c357fc68c8 doc: update readme file for new feature 2023-04-03 10:12:13 +08:00
dudaodong
bc25e7a037 feat: add IsPrintable 2023-03-31 12:00:32 +08:00
dudaodong
217350042b feat: add RemoveNonPrintable 2023-03-30 14:52:32 +08:00
dudaodong
e56a8a1ef5 feat: add IsASCII 2023-03-30 14:41:32 +08:00
dudaodong
6453f755a6 doc: add doc for IsPrime 2023-03-30 14:09:05 +08:00
dudaodong
027abd6ad5 feat: add IsPrime 2023-03-30 11:54:20 +08:00
dudaodong
91503b1656 comment TestEachWithBreak 2023-03-29 10:31:38 +08:00
dudaodong
e2522cd29b fix: fix TestEachWithBreak case 2023-03-29 10:24:41 +08:00
dudaodong
da69b77892 Merge branch 'main' of github.com:duke-git/lancet 2023-03-29 10:15:56 +08:00
dudaodong
05772d8d7d comment TestEachWithBreak 2023-03-28 20:08:05 +08:00
Mickls
3b497532f3 fix: The LastIndexOf method can never return the index of the first element (#83)
Co-authored-by: JiangCheng <jiangcheng@kezaihui.com>
2023-03-28 20:02:57 +08:00
dudaodong
1cd3be508c refactor rename async package name to 2023-03-24 11:43:56 +08:00
dudaodong
a41d461910 doc: update mathutil package doc 2023-03-24 11:23:45 +08:00
dudaodong
cde5946bf0 feat: add PointDistance 2023-03-23 17:49:07 +08:00
dudaodong
c28803b25e feat: add AngleToRadian and RadianToAngle 2023-03-23 17:41:31 +08:00
dudaodong
f09e521783 doc: add go playground demo 2023-03-22 21:06:01 +08:00
dudaodong
0aa41f337d fix: test failed 2023-03-20 16:39:56 +08:00
dudaodong
48814a720a fix: test failed in promise test 2023-03-20 16:26:21 +08:00
dudaodong
04b79b3dfe release v2.1.17 2023-03-20 16:17:52 +08:00
dudaodong
302007ebdf doc: update readme file for new feature 2023-03-20 16:12:58 +08:00
dudaodong
965e5fbcda feat: add Pop for set 2023-03-20 11:10:35 +08:00
dudaodong
70d0adde42 feat: add EachWithBreak for set 2023-03-20 10:49:14 +08:00
dudaodong
0b80074bb7 refactor: remove unused code 2023-03-20 10:35:37 +08:00
dudaodong
90945a0399 Merge branch 'main' into v2 2023-03-18 19:35:48 +08:00
zm
47dccd63af add the struct package english docs (#82) 2023-03-18 19:33:07 +08:00
dudaodong
4ae7e59829 feat: add example for promise 2023-03-17 16:03:35 +08:00
dudaodong
8f0c60cade test: add unit test for promise Race and Any function 2023-03-17 13:59:18 +08:00
dudaodong
3f6aef1432 fix: fix bug of IsNotNil function 2023-03-16 19:15:36 +08:00
dudaodong
a714e04470 test: add unit test for promise.All function 2023-03-16 19:11:30 +08:00
dudaodong
7456621153 fix: fix bug of IsNil function 2023-03-16 17:01:28 +08:00
dudaodong
73ac9825e9 test: add unit test for Promise 2023-03-16 16:55:32 +08:00
dudaodong
930bb9c839 feat: complete promise Any function 2023-03-16 15:59:06 +08:00
dudaodong
3d8f1be212 feat: add error_join.go to support join error under go1.19, for internal use. 2023-03-16 15:56:54 +08:00
dudaodong
13a4ed59fa doc: update comment for concurrency package 2023-03-16 15:35:26 +08:00
dudaodong
c799d10ce9 feat: add promise All, Race, Any methods 2023-03-16 15:34:28 +08:00
dudaodong
5ab322ade2 feat: add async package, promise implemention 2023-03-16 14:49:07 +08:00
dudaodong
d0ffc61842 doc: fix doc error 2023-03-16 10:36:06 +08:00
zm
5e66bc6227 [structs] change package structutil to structs (#81)
* refactor package structutil to structs

* add structs package zh-CN docs
2023-03-15 19:14:19 +08:00
dudaodong
7261b281ad refactor: add function comment for tag.go 2023-03-15 17:43:47 +08:00
dudaodong
f79693804b refactor: make package variable defaultTagName unexported 2023-03-15 15:08:56 +08:00
dudaodong
534c7a0abc refactor: make errInvalidStruct exported, change error.go to struct_internal.go 2023-03-15 14:58:34 +08:00
dudaodong
4eaff47d38 Merge branch 'main' into v2 2023-03-15 14:43:48 +08:00
dudaodong
3e019522c7 doc: add doc for ForEachWithBreak 2023-03-15 14:42:22 +08:00
dudaodong
0734f220b3 feat: add ForEachWithBreak support break for each loop 2023-03-15 14:38:33 +08:00
zm
2d2c277090 [StructUtil] add support that the Struct can nest any type to transform (#80)
* add support json tag attribute for StructToMap function

* add the structutil to provide more rich functions and fixed #77

* add support that the nested struct to map for structutil

* recover code

* add structutil unit test

* [StructUtil] add unit test
2023-03-15 14:26:34 +08:00
dudaodong
ef1e548dfc merge main 2023-03-13 19:44:44 +08:00
zm
924589d2da Add StructUtil for provide more rich functions (#79)
* add support json tag attribute for StructToMap function

* add the structutil to provide more rich functions and fixed #77
2023-03-13 19:28:37 +08:00
dudaodong
77f32f4cc6 Merge branch 'main' into v2 2023-03-11 20:14:32 +08:00
zm
1755dd249b add support json tag attribute for StructToMap function (#78) 2023-03-11 20:08:41 +08:00
dudaodong
7a25688ec1 doc: add doc for Range and RangeWithStep 2023-03-09 17:13:51 +08:00
dudaodong
51a6912eb3 feat: add RangeWithStep function 2023-03-06 18:05:58 +08:00
dudaodong
28d0428b50 feat: add Range function 2023-03-06 17:49:56 +08:00
dudaodong
6a9eb645bb fix: fix StructToUrlValues failed when tag contain omitempty 2023-03-06 17:07:57 +08:00
dudaodong
3857b342f6 fix: fix ExampleContext failed 2023-03-01 11:45:02 +08:00
dudaodong
71aa91a58d doc: add go playground demo 2023-03-01 11:39:27 +08:00
dudaodong
081908bce3 fix: fix goline error 2023-03-01 11:10:12 +08:00
dudaodong
66efe61834 release v2.1.16 2023-03-01 10:08:53 +08:00
dudaodong
94f7f3e0e5 doc: add new function docs 2023-02-28 15:03:02 +08:00
dudaodong
9d2c9806d1 feat: add ContainBy function 2023-02-24 14:19:10 +08:00
dudaodong
e523d4af6e doc: add doc for FilterMap and FlatMap 2023-02-24 11:23:02 +08:00
dudaodong
f3801bcd8f feat: add FlatMap 2023-02-24 11:14:59 +08:00
dudaodong
5767aad303 feat: add FilterMap 2023-02-24 10:55:27 +08:00
dudaodong
f7aaa1ed2a fix: fix issue #75 2023-02-23 11:43:41 +08:00
dudaodong
ef19a414bc doc: fix doc error 2023-02-23 10:58:43 +08:00
dudaodong
65704dea06 doc: fix doc error 2023-02-23 10:57:48 +08:00
dudaodong
5eac358bb0 doc: format code in doc file 2023-02-22 17:00:46 +08:00
dudaodong
8e36ef5cef doc: add doc for new map functions 2023-02-22 16:59:01 +08:00
dudaodong
21b0d2ec0e doc: add doc for WordCount and SplitWords function 2023-02-22 16:13:41 +08:00
dudaodong
32ca975204 feat: add OmitBy, OmitByKeys, OmitByValues 2023-02-21 14:52:10 +08:00
dudaodong
ec740e442c feat: add WordCount 2023-02-21 14:27:00 +08:00
dudaodong
ed98ad53ec feat: add SplitWords 2023-02-21 14:16:36 +08:00
dudaodong
af7b9d2710 doc: add doc for CopyProperties function 2023-02-20 14:10:55 +08:00
dudaodong
f08b368001 fix: update param type of CopyProperties 2023-02-20 14:04:45 +08:00
dudaodong
be8a0558f8 test: add examples for CopyProperties function 2023-02-20 12:00:49 +08:00
dudaodong
46de539e22 feat: add CopyProperties for merge properties between structs 2023-02-20 11:48:30 +08:00
dudaodong
36fb3abe9e test: add examples for maputil package function 2023-02-19 16:59:21 +08:00
dudaodong
fee8d325b7 doc: add doc for Pad function 2023-02-19 12:58:14 +08:00
dudaodong
8784be1583 feat: add Pad function 2023-02-19 12:37:26 +08:00
dudaodong
84cd68de7f fix: fix bugs of PadEnd and PadStart 2023-02-19 12:34:39 +08:00
dudaodong
a774c060ce feat: add MapKeys and MapValues 2023-02-17 15:41:58 +08:00
dudaodong
730f061b95 feat: add Entries FromEntries Transform 2023-02-17 15:24:46 +08:00
dudaodong
2eb08f3404 feat: add FilterByValues function 2023-02-17 11:59:44 +08:00
dudaodong
4c1496b648 feat: add FilterByKeys function 2023-02-17 11:57:09 +08:00
dudaodong
c2ae784f27 feat: add ValuesBy function 2023-02-17 11:48:11 +08:00
dudaodong
e71cecefea feat: add KeysBy function 2023-02-17 11:40:20 +08:00
dudaodong
572e53aa14 doc: add go playground demo for xerror package 2023-02-16 14:28:50 +08:00
dudaodong
ca9ecb9c8a update go playground demo 2023-02-16 14:10:24 +08:00
dudaodong
8bf0786abe release v2.1.15 2023-02-16 12:02:34 +08:00
dudaodong
61cb8395e3 doc: update doc for xerror package 2023-02-15 14:41:56 +08:00
dudaodong
dde8a41daf doc: update doc for xerror package 2023-02-15 14:41:41 +08:00
dudaodong
d7518e01af doc: fix ExampleXError_StackTrace bug 2023-02-15 12:04:14 +08:00
dudaodong
be9fa7acaa doc: update doc for xerror package 2023-02-15 11:58:33 +08:00
dudaodong
2629a731cc doc: update doc for xerror package 2023-02-15 11:53:21 +08:00
dudaodong
54c7f90b7f test: add examples for xerror package 2023-02-15 10:39:51 +08:00
dudaodong
770bc88b88 doc: update doc for xerror package 2023-02-15 10:14:26 +08:00
dudaodong
4cc1722f81 feat: fix AesEcbEncrypt and AesEcbDecrypt 2023-02-14 16:42:25 +08:00
dudaodong
d0260b2841 feat: add XError to support more contextual error handling 2023-02-14 16:32:57 +08:00
dudaodong
57bd64cae7 feat: add stack 2023-02-14 10:21:38 +08:00
dudaodong
c383719496 doc: add doc for DeepClone function 2023-02-12 19:24:56 +08:00
dudaodong
4b196a72b1 feat: add DeepClone 2023-02-10 15:50:28 +08:00
dudaodong
888381a06c doc: add drop and sort related function doc 2023-02-10 10:53:41 +08:00
dudaodong
a554eb7ef4 doc: add doc for sort function 2023-02-10 10:42:38 +08:00
dudaodong
26ff90122b doc: add doc for Drop function 2023-02-10 10:31:02 +08:00
dudaodong
75b27c6540 feat: add IsSortedByKey 2023-02-10 10:10:57 +08:00
dudaodong
a7e77fa98d feat: add IsSorted 2023-02-09 19:30:11 +08:00
dudaodong
abf392117a feat: add IsAscending, Isdescending 2023-02-09 19:25:58 +08:00
dudaodong
d231d9f572 feat: add DropRightWhile 2023-02-09 17:53:26 +08:00
dudaodong
97447d058e test: add test and example for DropWhile 2023-02-09 17:46:40 +08:00
dudaodong
040e112aa6 feat: add DropRight and DropWhile 2023-02-09 17:38:31 +08:00
dudaodong
afb021b4b5 feat: add Max, Min for stream 2023-02-08 15:20:29 +08:00
dudaodong
5075774000 feat: add Sorted for stream 2023-02-08 15:07:09 +08:00
dudaodong
6bba44dc50 feat: add Concat for stream 2023-02-07 10:47:28 +08:00
dudaodong
422022c74d feat: add Range for stream 2023-02-07 10:36:07 +08:00
dudaodong
87fcf97e2d feat: add FindFirst, Reverse for stream 2023-02-07 10:24:19 +08:00
dudaodong
05d1f348d4 refactor: update constant names 2023-02-06 19:39:31 +08:00
dudaodong
6f2f1f3004 fix: IsZeroValue support pointer 2023-02-06 19:36:36 +08:00
dudaodong
9cd9d1aeb5 release v2.1.14 2023-02-06 17:42:13 +08:00
dudaodong
71b27c0aa9 feat: add ForEach, Reduce for stream 2023-02-06 17:40:54 +08:00
dudaodong
09a379ec6d feat: add ForEach, Reduce for stream 2023-02-06 17:38:33 +08:00
dudaodong
b4b9b03835 feat: add NoneMatch, AllMatch, AnyMatch for stream 2023-02-06 17:24:17 +08:00
dudaodong
48c7794b01 feat: add Limit method of stream 2023-02-06 17:08:36 +08:00
dudaodong
8e3911833d doc: normalize documents 2023-02-06 11:42:03 +08:00
dudaodong
ebe494051b doc: add doc for some new methods of list 2023-02-06 11:35:32 +08:00
dudaodong
c35bda6a65 feat: add ListToMap for list 2023-02-06 11:06:46 +08:00
dudaodong
1fe4cdc429 feat: add Difference and SymmetricDifference for list 2023-02-06 10:55:42 +08:00
dudaodong
6a79e322e3 Merge branch 'main' into v2 2023-02-06 10:00:02 +08:00
Cai Zhijiang
17e8d2bb6d 部分城市有4位区号+8位号码 (#69) 2023-02-06 09:47:21 +08:00
燕归来
325be0d6a1 feat: add func Iterator, ForEach, RetainAll and DeleteAll for List structure (#71) 2023-02-06 09:46:25 +08:00
dudaodong
ea0f96a8c0 feat: add Skip for stream 2023-01-30 16:56:33 +08:00
dudaodong
82cbb54787 feat: add FromChannel for create stream 2023-01-17 16:47:20 +08:00
dudaodong
585d33cafa feat: add Generate for create stream 2023-01-17 16:31:44 +08:00
dudaodong
bc4cf35e15 feat: add Filter, Map, Count for stream 2023-01-17 14:59:47 +08:00
dudaodong
a3bc20af1d feat: add Distinct 2023-01-17 11:39:05 +08:00
dudaodong
61338b6b46 feat: add Stream package 2023-01-17 11:25:15 +08:00
dudaodong
bc3c080ac3 doc: normalize document 2023-01-15 12:43:00 +08:00
dudaodong
d3fab15af3 refactor: clean structure for netutil package 2023-01-14 14:39:06 +08:00
dudaodong
6e3e411d46 doc: normalize document 2023-01-14 12:48:39 +08:00
dudaodong
f976941e36 doc: normalize document 2023-01-14 12:32:27 +08:00
dudaodong
4c5524354c doc: normalize documents 2023-01-13 14:36:33 +08:00
dudaodong
2c6e9a3fb9 doc: format document 2023-01-13 11:01:22 +08:00
dudaodong
5f2c3edff4 doc: format document 2023-01-13 10:57:40 +08:00
dudaodong
b422d98702 doc: format document 2023-01-13 10:48:21 +08:00
dudaodong
6f27e0bfbf doc: format document 2023-01-13 10:47:47 +08:00
dudaodong
ce3b6b461e doc: format document 2023-01-13 10:43:16 +08:00
dudaodong
0d6ad4f0d2 doc: format document 2023-01-13 10:41:47 +08:00
dudaodong
c875a7f8b8 doc: format document 2023-01-13 10:39:39 +08:00
dudaodong
0c62d117a1 doc: format document 2023-01-13 10:12:37 +08:00
dudaodong
c260ce493d doc: normalize documents 2023-01-12 15:44:34 +08:00
dudaodong
9ffe96d3ef update readme file 2023-01-12 11:32:43 +08:00
dudaodong
d4bba76dc8 fix: fix ExampleSchedule failed 2023-01-09 11:25:14 +08:00
dudaodong
adaa3ebc43 Merge branch 'main' into v2 2023-01-09 11:16:13 +08:00
dudaodong
61d38ae3b8 doc: update doc for algorithm package 2023-01-09 11:15:25 +08:00
dudaodong
c85d910044 add playground demo 2023-01-09 11:02:41 +08:00
dudaodong
d95a7c6101 fix: fix example function bug in function package 2023-01-08 21:09:41 +08:00
dudaodong
bce3641ec6 fix: fix example function bug in datetime package 2023-01-08 21:04:39 +08:00
dudaodong
ceb134b2fd fix: fix example function bug in datetime package 2023-01-08 21:01:05 +08:00
dudaodong
4b9b1d32c5 release v2.1.13 2023-01-08 20:45:43 +08:00
dudaodong
395e0883c7 fix: fix example test failed of CreateFile 2023-01-08 20:44:23 +08:00
dudaodong
a930511054 test&doc: add example and update doc for xerror package 2023-01-08 20:36:32 +08:00
dudaodong
7380721ccc test&doc: add example and update doc for xerror package 2023-01-08 20:36:17 +08:00
dudaodong
936011dc3b fix: fix bug in file test 2023-01-08 20:25:11 +08:00
dudaodong
bc0ee08f38 test&doc: add example and update doc for datetime package 2023-01-08 20:16:04 +08:00
dudaodong
f1afd753d4 test&doc: add example and update doc for netutil package 2023-01-08 16:57:03 +08:00
dudaodong
b05a0a91c3 test&doc: add example and update doc for netutil package 2023-01-08 16:54:29 +08:00
dudaodong
da2eb66657 doc: update doc for datastructure package 2023-01-08 15:42:34 +08:00
dudaodong
ad20159de2 doc: update doc for datastructure package 2023-01-08 15:41:59 +08:00
dudaodong
278733d3d1 Todo: add todo for playground demo of function 2023-01-07 18:27:07 +08:00
dudaodong
2f184907ff doc: update document for cryptor package 2023-01-07 18:18:23 +08:00
dudaodong
50fcc718ee test: add example for cryptor package 2023-01-07 17:34:42 +08:00
dudaodong
cc68feb52d doc&test: add example and update doc for retry package 2023-01-07 14:22:07 +08:00
dudaodong
ca2a51b37e test&doc: add example and update doc for function package 2023-01-06 17:12:32 +08:00
dudaodong
be444f521d test: add examples for function package 2023-01-06 16:12:44 +08:00
dudaodong
e21dd07d46 doc: update doc for validator package 2023-01-06 14:14:00 +08:00
dudaodong
4044deac70 test: add examples for validator package 2023-01-06 11:31:51 +08:00
dudaodong
d9c6294775 fix: fix goline error 2023-01-05 14:55:47 +08:00
dudaodong
d8505d1a5f doc: update doc for slice package 2023-01-05 14:49:30 +08:00
dudaodong
6498c7d68a test: add examples for slice package 2023-01-05 11:26:56 +08:00
dudaodong
5f0211f0c4 test: add examples for slice package 2023-01-04 14:44:10 +08:00
dudaodong
1de2e2cedd test: add examples for random package 2023-01-03 15:10:42 +08:00
dudaodong
c8f8b1b7d9 doc: update doc for fileutil package 2023-01-03 14:33:29 +08:00
dudaodong
3062eb7789 test: add examples for fileutil package 2023-01-03 11:55:23 +08:00
dudaodong
64d5486cc6 doc: add example and update doc for formatter package 2023-01-02 15:35:09 +08:00
dudaodong
6d57891f66 doc: update document for mathutil package 2023-01-02 15:19:47 +08:00
dudaodong
927245e47f test: add example for mathutil package 2023-01-02 14:59:49 +08:00
dudaodong
31fdbee0b5 doc: update document for maputil function 2023-01-02 14:21:23 +08:00
dudaodong
3712819994 test: add exmaple for maputil package 2023-01-02 14:06:12 +08:00
dudaodong
6d7dec1cea doc: update doc for convertor package 2023-01-01 22:00:23 +08:00
dudaodong
9b6a004dbc test: add example for convertor package 2023-01-01 21:24:24 +08:00
dudaodong
3ad6f4bd9e doc: update doc for condition package 2022-12-31 16:47:18 +08:00
dudaodong
b8c6746f31 test: add example for condition package 2022-12-31 16:26:43 +08:00
dudaodong
0bf8bbf4cb doc: add example and update docment for channel 2022-12-31 13:38:52 +08:00
dudaodong
a6ba1028c5 doc: add example and update docment for channel 2022-12-31 13:35:44 +08:00
dudaodong
cc54dd7ec9 doc: add example and update docment for channel 2022-12-31 13:12:16 +08:00
dudaodong
54834dba4c doc: update doc for system package 2022-12-30 14:28:49 +08:00
dudaodong
e996d4c945 test: add examples for system package 2022-12-30 14:14:37 +08:00
dudaodong
ae92ae7666 test: add examples for system package 2022-12-30 14:14:22 +08:00
dudaodong
526e568d0e doc: add doc for Substring 2022-12-29 20:03:59 +08:00
dudaodong
1dc5e8ac23 feat: add Substring function 2022-12-29 19:55:40 +08:00
dudaodong
b5f7b0e670 doc: update document for algorithm package 2022-12-29 15:32:47 +08:00
dudaodong
39c576248c test: add examples for lrucache 2022-12-29 11:43:14 +08:00
dudaodong
eb164d1536 test: add examples for lrucache function 2022-12-29 11:43:04 +08:00
dudaodong
68e170080c test: add examples for sort function 2022-12-28 20:18:05 +08:00
dudaodong
652b09135c test: add examples for search function 2022-12-28 17:58:56 +08:00
dudaodong
bff24c89bc doc: update strutil document 2022-12-27 17:26:34 +08:00
dudaodong
49a460eef8 doc: update document and add playgound example for strutil package 2022-12-27 16:57:32 +08:00
dudaodong
a58e52e53c test: add example function for strutil package 2022-12-27 15:40:51 +08:00
dudaodong
b07356423f fix: update format 2022-12-26 17:57:06 +08:00
dudaodong
005dd9d2ab fix: fix word misspelling 2022-12-26 17:51:30 +08:00
dudaodong
65315dafb1 feat: add take iterator 2022-12-26 17:20:14 +08:00
dudaodong
b06fb6736d feat: add reduce for iterator 2022-12-26 16:55:24 +08:00
dudaodong
b9f0854950 feat: add join iterator 2022-12-26 15:58:44 +08:00
dudaodong
6a2dd328ad feat: add test for map iterator and filter iterator 2022-12-26 15:13:35 +08:00
dudaodong
dd1147f6d0 feat: add Channel iterator 2022-12-26 15:02:28 +08:00
dudaodong
6da7ce64af test: add unit test for RangeIterator 2022-12-26 14:24:23 +08:00
dudaodong
b7e5d946f1 release v2.1.12 2022-12-15 16:55:20 +08:00
dudaodong
687db4ce79 update readme file 2022-12-15 16:44:30 +08:00
dudaodong
a9a4bb8841 update readme file 2022-12-15 16:42:22 +08:00
dudaodong
bc6cb5f61b fix code issue in doc 2022-12-15 16:38:50 +08:00
dudaodong
2c57266f8e test: update some test functions 2022-12-15 15:22:01 +08:00
dudaodong
57e49c9520 doc: update document for funcations CamelCase/KebabCase/UpperKebabCase/SnakeCase/UpperSnakeCase 2022-12-14 21:42:43 +08:00
dudaodong
985c9a5d9a refactor: refact CamelCase function 2022-12-14 21:27:29 +08:00
dudaodong
5cfb11f036 feat: fix and add SnakeCase/UpperSnakeCase 2022-12-14 21:17:30 +08:00
dudaodong
5b3d48a1e7 feat: fix and add KebabCase/UpperKebabCase 2022-12-14 21:09:22 +08:00
dudaodong
d0576e028f clean code 2022-12-13 19:52:37 +08:00
dudaodong
76bdec2b54 test: add cases for Capitalize 2022-12-13 16:16:16 +08:00
dudaodong
fa20aba3a7 fix: fix CamelCase function bug 2022-12-13 14:23:32 +08:00
dudaodong
7a4a429e23 test: add cases for Capitalize 2022-12-12 21:11:19 +08:00
dudaodong
a70ec6ad1e refactor: use constraints from golang.org/x/exp/constraints 2022-12-11 11:25:34 +08:00
dudaodong
e435fa271b doc: update doc for Comma 2022-12-10 21:03:16 +08:00
dudaodong
1533d00891 refactor: update Comma function 2022-12-10 20:59:27 +08:00
dudaodong
8b99641de0 test: remove print info in test function 2022-12-10 19:19:49 +08:00
dudaodong
251f899f18 fix: TestExecCommand failed in linux/macos 2022-12-10 19:12:19 +08:00
dudaodong
00407e5182 fix: fix lint issue 2022-12-10 19:09:18 +08:00
dudaodong
4e457ad672 change: change update and insert function in link datastruture 2022-12-10 17:45:29 +08:00
dudaodong
7d8d9c3543 fix: fix lint issue 2022-12-10 16:55:06 +08:00
dudaodong
1197e8d1b6 fix: fix lint issue 2022-12-10 16:53:41 +08:00
dudaodong
13bbe19ab2 fix: fix lint issue 2022-12-10 16:41:40 +08:00
dudaodong
2725575d2f doc: add doc for IsGBK' 2022-12-09 19:28:45 +08:00
dudaodong
037d2729ce doc: update doc for ExecCommand 2022-12-09 19:20:15 +08:00
dudaodong
09d98745b0 fix: fix ExecCommand bug in windows 2022-12-09 19:10:55 +08:00
dudaodong
af5cfe6da1 feat: add IsGBK validator 2022-12-09 17:36:59 +08:00
Mickls
d59259bbe0 feat: A more reasonable IndexOf function (#66) 2022-12-09 11:31:40 +08:00
dudaodong
3189628d54 fix: fix AesCfbDecrypt 2022-12-08 15:07:40 +08:00
dudaodong
62c5e251a5 remove website 2022-12-08 14:55:21 +08:00
dudaodong
6e6444c8c0 refator: clean code 2022-12-06 20:20:36 +08:00
dudaodong
dd613e98b2 refator: clean code 2022-12-06 20:00:41 +08:00
dudaodong
2d905ab03e fix: fix word misspelling 2022-12-04 11:25:02 +08:00
dudaodong
205fedb197 release v2.1.11 2022-12-04 11:11:05 +08:00
dudaodong
4c864da62d doc: update readme file 2022-12-04 11:10:19 +08:00
dudaodong
263ab7e316 clean code 2022-12-03 14:41:58 +08:00
dudaodong
809b7a53df doc: update docment for slice package 2022-12-03 14:33:15 +08:00
dudaodong
61c43daabb doc: add doc for Count and CountBy function 2022-12-03 14:20:37 +08:00
dudaodong
18914ee2cd feat: add deprecat IntSlice, InterfaceSlice and StringSlice 2022-12-03 14:00:44 +08:00
dudaodong
0a8058956f feat: add Count and CountBy function 2022-12-03 13:04:12 +08:00
dudaodong
a044da7d2f refactor: clean code 2022-12-03 12:58:25 +08:00
dudaodong
f8b785c4cb doc: add doc for Sort and SortBy 2022-12-02 15:47:16 +08:00
dudaodong
82c8a04c35 make SortByField function deprecate 2022-12-02 15:17:08 +08:00
dudaodong
280ecb5cef feat: add SortBy function for slice 2022-12-02 14:53:57 +08:00
dudaodong
ec27ad4c40 feat: add Sort function for slice 2022-12-01 22:46:56 +08:00
dudaodong
d66f92cd68 doc: update slice and convertor document 2022-12-01 11:43:10 +08:00
dudaodong
d8ed692651 refactor: code improvement, ToString, Contain, Compact 2022-12-01 11:38:25 +08:00
dudaodong
a16de97d1d refactor: code improvement, ToString, Contain, Compact 2022-12-01 11:38:14 +08:00
dudaodong
6f458e4367 doc: add doc for NewSetFromSlice 2022-11-30 12:34:56 +08:00
dudaodong
37c7508ad0 feat: add NewSetFromSlice function for set 2022-11-30 12:00:55 +08:00
dudaodong
acb5844b15 docs: add doc for AddIfNotExist and AddIfNotExistBy 2022-11-30 11:54:04 +08:00
dudaodong
76f4eeea16 docs: add doc for Merge and Repeat 2022-11-30 11:35:32 +08:00
dudaodong
5466a23019 refactor: fix bad code smell 2022-11-30 11:20:13 +08:00
dudaodong
5692982dd1 feat: add AddIfNotExistBy function for set 2022-11-29 23:59:22 +08:00
dudaodong
c39c8914fb refactor: fix bad code smell 2022-11-29 23:50:17 +08:00
dudaodong
29bdca1bd2 feat: add AddIfNotExist function for set 2022-11-29 23:39:29 +08:00
dudaodong
eb66d038ac feat: update iterator package 2022-11-29 13:58:48 +08:00
dudaodong
a99ada5ee1 feat: add iterator package 2022-11-28 19:24:25 +08:00
dudaodong
a87faf5453 feat: add Repeate function for slice 2022-11-27 21:43:38 +08:00
dudaodong
ab6fec2f69 refactor: fix bad code smell 2022-11-27 21:36:30 +08:00
dudaodong
7b290989f5 feat: add Merge function 2022-11-27 20:20:00 +08:00
dudaodong
5722c724e6 Merge branch 'main' into v2 2022-11-27 19:29:33 +08:00
dudaodong
f84584ca04 doc: add website link 2022-11-27 18:44:25 +08:00
dudaodong
80cbbdc787 docs: format index content 2022-11-26 17:37:09 +08:00
dudaodong
be148e07ba fix: fix chunk slice bug 2022-11-26 16:21:57 +08:00
dudaodong
d36ab5cc3a fix: go report issue ineffassign 2022-11-18 17:05:54 +08:00
dudaodong
2b17329094 release v2.1.10 2022-11-17 16:11:48 +08:00
dudaodong
f869a0a670 fix: issue#62: fix ZipSlip bug 2022-11-16 16:04:38 +08:00
dudaodong
be000a4bd6 fix: issue#62: fix ZipSlip bug 2022-11-16 15:08:42 +08:00
DuDaoDong
81efa800ea Create SECURITY.md 2022-11-16 11:31:50 +08:00
dudaodong
a783de57a8 release v2.1.9 2022-11-08 15:22:37 +08:00
dudaodong
a622959a78 docs: add new functions readme 2022-11-08 15:21:31 +08:00
dudaodong
f709dd53ce docs: update doc for random package 2022-11-08 14:48:02 +08:00
dudaodong
ee9e9625e2 docs: add doc for UnionBy and KeyBy 2022-11-08 14:21:17 +08:00
dudaodong
84da7d4f27 feat: add RandUpper, RandLower, RandNumeral, and RandNumeralOrLetter 2022-11-06 20:04:52 +08:00
dudaodong
089fd4e13c feat: add UnionBy for slice 2022-11-05 19:45:34 +08:00
dudaodong
6c40e02324 refactor: clean code 2022-11-05 19:33:56 +08:00
dudaodong
a270b1b634 feat: make code clear 2022-11-05 17:09:56 +08:00
dudaodong
260fb795d3 feat: add KeyBy for slice 2022-11-05 16:52:31 +08:00
dudaodong
8c036f830c refactor: remove unuseful code 2022-11-05 16:39:42 +08:00
dudaodong
bf332b9f1c release v2.1.8 2022-10-21 16:22:52 +08:00
dudaodong
6b6cd66f9f Merge branch 'main' into v2 2022-10-19 17:31:51 +08:00
燕归来
5399c2290e feat: support type alias for Number constraint (#60) 2022-10-19 17:21:53 +08:00
dudaodong
6248293c49 doc: add docment for Replace and ReplaceAll 2022-10-17 10:40:35 +08:00
dudaodong
eced25b76d test: add unit test for Replace and ReplaceAll 2022-10-17 10:26:54 +08:00
dudaodong
96a4327aa7 fix: fix some static check issues 2022-10-15 15:03:24 +08:00
dudaodong
fcfbdea597 feat: add Replace and ReplaceAll for slice 2022-10-15 14:49:49 +08:00
dudaodong
87896f917a doc: add document for pipeline 2022-10-15 12:36:11 +08:00
dudaodong
b8563ed646 feat: add Pipeline function 2022-10-15 12:29:47 +08:00
dudaodong
1ccf0af2b3 doc: update document for datetime package 2022-10-14 10:20:38 +08:00
dudaodong
6314889c6a release v2.1.7 2022-10-14 10:08:43 +08:00
dudaodong
294bd5a5ed doc: update document for hashmap 2022-10-11 15:41:09 +08:00
dudaodong
ca44815fd5 Merge branch 'main' into v2 2022-10-11 15:02:28 +08:00
dudaodong
bcd1cabf80 feat: add Keys and Values function for hashmap 2022-10-10 16:34:05 +08:00
CyJaySong
4edefcca67 expand BeginOfWeek、EndOfWeek (#59) 2022-10-10 15:44:22 +08:00
dudaodong
fab24c8d12 feat: add Iterate for hashmap datastructure 2022-10-10 15:25:30 +08:00
dudaodong
604acd9b07 doc:add lancet api doc website 2022-09-29 18:41:22 +08:00
dudaodong
531cb19fd1 release v2.1.6 2022-08-31 17:07:32 +08:00
dudaodong
4f0161ca53 doc: add source code link for http_client 2022-08-31 15:56:41 +08:00
dudaodong
73362b7f69 doc: update readme file 2022-08-31 11:40:24 +08:00
dudaodong
b289f2975b doc: add document for functions in netutil/http_client.go 2022-08-31 11:20:22 +08:00
dudaodong
90ce2705ca test: add unit test for http client 2022-08-30 17:33:58 +08:00
dudaodong
35e1d09ce3 feat: add http client for sending http request 2022-08-30 17:22:17 +08:00
dudaodong
d67d8fad3a doc: fix missing word in file condition.md 2022-08-29 14:02:30 +08:00
dudaodong
c5e6d01a31 update readme file: add new package and function 2022-08-29 11:50:37 +08:00
dudaodong
6d6c3f692f refactor: change TernaryOperator signature 2022-08-29 11:27:51 +08:00
dudaodong
c695837b16 doc: add document for package condition 2022-08-29 11:23:02 +08:00
dudaodong
ef28b52963 fix: fix regex isChineseMobileRegexMatcher not work 2022-08-28 20:58:34 +08:00
dudaodong
18b2b6ff7c feat: add functons: And, Or, Xor, Nor, Xnor, Nand 2022-08-28 20:57:28 +08:00
dudaodong
6a05a123f4 feat: add Bool function to check if a value is truthy or falsy 2022-08-28 20:38:19 +08:00
dudaodong
f7c33f258d Merge branch 'main' into v2 2022-08-27 19:25:24 +08:00
dudaodong
04058dd7da refactor: change common package to condition package 2022-08-27 19:19:34 +08:00
郑一诺她爸
72e1d92fa1 TernaryOperator Reduce if else operations (#56)
* feat:(slice add AppendIfAbsent function)

* feat:(slice add TernaryOperator function)

Co-authored-by: george.zheng <george.zheng@ambergroup.io>
2022-08-27 19:12:03 +08:00
dudaodong
063df0f0d1 doc: add docment for IsZeroValue function 2022-08-27 19:04:57 +08:00
dudaodong
fc10689b25 feat: add IsZeroValue function 2022-08-27 18:57:45 +08:00
dudaodong
9239bcfdc3 doc: fmt code example 2022-08-26 16:34:20 +08:00
dudaodong
c984815dea doc: fmt code example 2022-08-26 16:29:55 +08:00
dudaodong
301cb5db87 doc: add hashmap 2022-08-26 16:26:16 +08:00
dudaodong
cc1bacff74 doc: add document for EncodeByte and DecodeByte 2022-08-26 10:54:51 +08:00
dudaodong
f49f75b371 test: add unit test for EncodeByte and DecodeByte 2022-08-26 10:47:25 +08:00
dudaodong
b30f4a7bab feat: add DecodeByte 2022-08-26 10:35:54 +08:00
dudaodong
982cb8932b feat: add EncodeByte 2022-08-26 10:34:42 +08:00
dudaodong
9107eb4b32 fix: fix resize bug in HashMap 2022-08-25 14:04:26 +08:00
dudaodong
1acf977b24 release v2.1.5 2022-08-25 11:11:36 +08:00
dudaodong
5c878d0873 doc: change ReverseStr to Reverse in all docment 2022-08-24 10:37:56 +08:00
dudaodong
ab0716a472 refactor: change ReverseStr to Reverse in strutil package 2022-08-24 10:35:18 +08:00
dudaodong
3c2e0ca5b3 doc: add document for hashmap 2022-08-24 10:28:57 +08:00
dudaodong
551e66ba29 refactor: update HashMap Put method 2022-08-24 10:20:02 +08:00
dudaodong
4eeeabb227 feat: add Delete method for hashmap 2022-08-23 15:07:54 +08:00
dudaodong
b3437fdddf feat: add Delete method for hashmap 2022-08-23 15:07:13 +08:00
dudaodong
312dcab369 feat: add Delete method for hashmap 2022-08-23 15:05:32 +08:00
dudaodong
0cb89f4f46 test: add unit test for HashMap 2022-08-23 14:15:51 +08:00
dudaodong
a2dec87995 feat: add Put method for hashmap 2022-08-23 14:11:14 +08:00
dudaodong
9094cb29bf feat: add new HashMap data structure 2022-08-22 15:21:30 +08:00
dudaodong
ddb992ad2f refactor: BuildMaxHeap 2022-08-22 11:10:17 +08:00
dudaodong
24f18aaaec refactor: change function sign for QuickSort 2022-08-22 11:02:25 +08:00
dudaodong
36169874e5 refactor: change sort file name 2022-08-22 10:58:47 +08:00
dudaodong
23aeb6ac99 refactor: change sort file name 2022-08-22 10:57:04 +08:00
dudaodong
a1984f0a59 feat: add BuildMaxHeap 2022-08-22 10:51:37 +08:00
IceCafeCup
c95db23d2c feat: Set struct uses empty struct as value (#55) 2022-08-13 12:39:30 +08:00
Abirdcfly
fbf251d805 delete minor unreachable code caused by log.Fatal (#54)
Signed-off-by: Abirdcfly <fp544037857@gmail.com>
2022-08-10 14:19:21 +08:00
dudaodong
337f08a04b fix: issue #53 2022-08-06 17:41:45 +08:00
dudaodong
c6a7371049 change lastest v1 version 2022-08-03 10:02:46 +08:00
dudaodong
b697858038 release v2.1.4 2022-08-03 09:56:02 +08:00
dudaodong
5446f7e33c doc: add document for list methods 2022-07-28 12:56:11 +08:00
dudaodong
553f63e76b refactor: update param name in AppendIfAbsent 2022-07-27 15:45:34 +08:00
dudaodong
fc3e94df58 doc: add document for AppendIfAbsent function 2022-07-27 15:44:54 +08:00
dudaodong
5c66f38a5b test: fix test function name TestAppendIfAbsent 2022-07-27 15:38:51 +08:00
郑一诺她爸
70e213b3f7 feat:(slice add AppendIfAbsent function) (#52)
Co-authored-by: george.zheng <george.zheng@ambergroup.io>
2022-07-25 20:52:33 +08:00
donutloop
c1b7500bcb List: add cap (#51)
Cap return cap of the inner data
2022-07-25 20:50:31 +08:00
donutloop
3d7600a9e4 list: add DeleteIf method (#50)
DeleteIf delete all satisfying f(data[i]), returns count of removed elements
2022-07-23 18:52:29 +08:00
donutloop
0299c454ab list: add sublist method (#49)
SubList returns a sub list of the original list between the specified fromIndex, inclusive, and toIndex, exclusive.
2022-07-23 15:00:04 +08:00
dudaodong
741af66404 test: add todo fix for TestSchedule 2022-07-22 10:30:44 +08:00
dudaodong
ef5d0379a1 doc: fix missing word 2022-07-22 10:24:34 +08:00
dudaodong
105ab49763 refactor: change slice Contain logic, convert to map first, then check existence 2022-07-22 10:22:34 +08:00
dudaodong
b5ba9ba573 fix: fix TestMapToSlice function result independent of order of elements 2022-07-22 10:17:23 +08:00
donutloop
ac3baac5c6 list: Add LastIndexOfFunc and IndexOfFunc method (#48)
* LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying f(data[i])
* IndexOfFunc returns the first index satisfying f(v)
2022-07-22 10:04:31 +08:00
donutloop
a82b5dd206 list: add LastIndexOf (#46)
Add LastIndexOf method too List and fix doc

LastIndexOf returns the index of the last occurrence of the value in this list.
if not found return -1
2022-07-21 15:19:18 +08:00
CyJaySong
ac0fb5ef25 Update convertor_zh-CN.md (#45)
fix docs
2022-07-21 11:22:30 +08:00
dudaodong
9bd1c205fe release: update readme file and release v2.1.3 2022-07-21 11:16:19 +08:00
dudaodong
6ccf9fd3cf doc: add document for GetOsBits function 2022-07-21 11:05:17 +08:00
dudaodong
c02ef2c62d doc: format code in markdown file 2022-07-20 19:42:17 +08:00
dudaodong
6414031754 doc: fix mistaken link to map source file 2022-07-20 19:38:14 +08:00
dudaodong
5336130570 doc: add funcation IsDisjoint 2022-07-20 19:33:37 +08:00
dudaodong
bfa46d46a2 doc: format code in markdown file 2022-07-20 19:33:02 +08:00
dudaodong
aab28b914c Merge branch 'main' into v2 2022-07-20 17:48:26 +08:00
donutloop
336e454ce7 maputil: IsDisjoint (#43)
Verifies map a and map b have no keys in common.

if it returns true then the maps are disjoint otherwise a join in some form available
2022-07-20 17:26:54 +08:00
anyanfei
35a50bd792 System: Add the system bits judgment (#44)
Co-authored-by: anyanfei <anyanfei@yungengxin.com>
2022-07-20 17:25:14 +08:00
dudaodong
2fd23f02f6 doc: format code in markdown file 2022-07-20 15:44:33 +08:00
dudaodong
aad5b447c9 doc: format code in markdown file 2022-07-20 15:13:44 +08:00
dudaodong
cece13e929 fix: fix aes/des cbc crypto iv bug 2022-07-20 11:42:52 +08:00
donutloop
ecf325a06c maputil: preallocate len and cap for keys and values slice (#41)
Length of keys and values is know in advance, use this knowledge to preallocate memory.
Space usage increases by const factor
2022-07-19 15:04:19 +08:00
dudaodong
44370aef5e refactor: change variable name to 2022-07-18 15:52:51 +08:00
dudaodong
1782b11ec4 refactor: refact Intersection function 2022-07-18 15:33:45 +08:00
dudaodong
fe6495123c doc: add document for EncodeUrl function 2022-07-18 11:31:12 +08:00
dudaodong
d442f564ce Merge branch 'main' into v2 2022-07-17 13:35:12 +08:00
dudaodong
f35446cc13 docs: fix word mistaken 2022-07-17 13:34:19 +08:00
dudaodong
506a8b4776 feat: add EncodeUrl function 2022-07-15 16:29:41 +08:00
dudaodong
47ecfbfd5f docs: add doc for Flatten function 2022-07-14 17:25:47 +08:00
dudaodong
82f7401368 feat: add Flatten function 2022-07-14 11:53:16 +08:00
dudaodong
068c878d1e release v2.1.2 2022-07-13 10:40:03 +08:00
dudaodong
47b2402345 docs: update readme file add new functions 2022-07-13 10:32:03 +08:00
dudaodong
a245716f99 docs: add doc for new function: ToChannel ToMap MapToSlice 2022-07-13 10:26:49 +08:00
dudaodong
71c85bb8f0 docs: add doc for functions GetRequestPublicIp and IsInternalIP 2022-07-13 10:09:23 +08:00
dudaodong
5edafec393 Merge branch 'main' into v2 2022-07-13 09:55:04 +08:00
tangqiu0205
957878dd98 feat: add func GetRequestPublicIp and IsInternalIP (#40)
* add: add func GetRequestPublicIp and IsInternalIP

* fix: fix test fileutil fail

Co-authored-by: zhanghu <305835360@qq.com>
2022-07-13 09:52:35 +08:00
dudaodong
12e979cf3c feat: add ToMapToSlice function 2022-07-11 11:28:46 +08:00
dudaodong
ded42f8ff5 feat: add ToChannel function 2022-07-11 11:11:16 +08:00
dudaodong
d06cde3fcf feat: add ToMap function 2022-07-11 10:27:38 +08:00
dudaodong
9cd8bfb8e0 docs: compress log png 2022-07-08 10:52:53 +08:00
dudaodong
51c9877224 docs: update change param type to for some slice functions 2022-07-07 17:47:02 +08:00
dudaodong
f7f6427919 merge main branch 2022-07-07 16:35:46 +08:00
tangqiu0205
56b6844a2d update: update type constraint any -> Comparable. (#37)
* update: update type constraint any -> Comparable.

* update: update type constraint any -> Comparable.

Co-authored-by: zhanghu <305835360@qq.com>
2022-07-07 16:31:48 +08:00
dudaodong
cce56f0479 docs: update query string type in http request 2022-07-07 11:21:16 +08:00
dudaodong
3dbd7d8980 fix: sending post request with header multipart/form-data support query string type is map[string]string 2022-07-07 10:23:15 +08:00
dudaodong
3625921912 fix: fix missused function ToSlice -> ToSlicePointer 2022-07-06 11:31:11 +08:00
dudaodong
1faaf54986 release v2.1.1 2022-07-06 11:25:02 +08:00
dudaodong
efe1fba267 docs: add doc for heap datastructure 2022-07-06 11:22:00 +08:00
dudaodong
0c43cb3f68 docs: update convertor package doc 2022-07-06 11:17:45 +08:00
dudaodong
c25111e349 docs: add doc for ToSlice and ToSlicePointer function 2022-07-06 11:15:51 +08:00
dudaodong
a61ab5716e docs: add doc for ToPointer function 2022-07-06 11:04:30 +08:00
dudaodong
628404dc7f fix: fix lint error 2022-07-06 10:57:45 +08:00
tangqiu0205
bab0a27e40 Add ToPointer,ToSlicePointer,ToSlice func (#36)
* Add ToPointer func

* Add ToSlice and ToSlicePointer func

Co-authored-by: zhanghu <305835360@qq.com>
2022-07-05 19:15:54 +08:00
dudaodong
fc0e104591 Merge branch 'main' into v2 2022-06-30 14:12:54 +08:00
dudaodong
e83e9a1651 docs: add doc for MaxHeap 2022-06-29 16:43:56 +08:00
dudaodong
3d2e6295c4 docs: add doc for MaxHeap 2022-06-29 15:40:14 +08:00
dudaodong
221fe44e63 docs: add doc for MaxHeap 2022-06-29 15:37:42 +08:00
dudaodong
7930f517ae feat: add MaxHeap func PrintStructure 2022-06-29 11:08:07 +08:00
dudaodong
0f61321d5b test: add unit test for MaxHeap 2022-06-27 17:55:35 +08:00
dudaodong
7ddeeb51e5 feat: add MaxHeap 2022-06-27 17:01:17 +08:00
dudaodong
484d2845b3 feat: add MaxHeap 2022-06-27 16:27:34 +08:00
dudaodong
bbbc6b6941 docs: update go version badge 2022-06-24 09:39:38 +08:00
dudaodong
acf028cdcd release v2.1.0 2022-06-22 16:10:54 +08:00
dudaodong
31e43ec356 docs: add doc for UniqueBy function 2022-06-22 16:08:37 +08:00
dudaodong
9f45e68fef test: add fuzz test for Equal function 2022-06-21 14:17:33 +08:00
dudaodong
d2df99a6f0 feat: add UniqueBy function in slice.go 2022-06-17 15:18:04 +08:00
dudaodong
713c341831 docs: update comment for Size function of ArrayQueue 2022-06-16 18:03:50 +08:00
dudaodong
0b05a6dd6f release: v2.0.9 2022-06-15 15:27:01 +08:00
dudaodong
d21c101caf refactor: change function name EqualWithFunc to EqualWith 2022-06-15 15:25:44 +08:00
dudaodong
bf49db60d9 docs: change function name EqualWithFunc to EqualWith 2022-06-15 15:24:58 +08:00
dudaodong
d57fa3b603 docs: update readme file add new functions 2022-06-15 15:23:30 +08:00
dudaodong
b3a29ce82d docs: add doc for function Equal and EqualWithFunc 2022-06-13 15:14:37 +08:00
dudaodong
1d8a37d6e8 docs: add doc for function CreateDir 2022-06-13 15:00:12 +08:00
dudaodong
ef2d8e14b0 feat: add EqualWithFunc function for slice 2022-06-13 14:15:32 +08:00
dudaodong
0cbb3dd97e feat: add Equal function for slice 2022-06-13 14:06:20 +08:00
dudaodong
38a7f9423f fmt: gofmt package datastructure/queue 2022-06-08 14:40:37 +08:00
dudaodong
2c0ab9e922 feat: add CreateDir function in file.go 2022-06-08 14:38:35 +08:00
dudaodong
9aa3b742ff docs: add doc for package datastructure 2022-06-07 14:54:23 +08:00
dudaodong
6bfaba750d docs: add doc tree-zh-CN.md 2022-06-07 14:39:32 +08:00
dudaodong
69f8bee935 docs: add doc tree datastructure 2022-06-07 14:17:34 +08:00
dudaodong
07d04a9cd0 refactor: change function name 2022-06-07 13:48:32 +08:00
dudaodong
6f17ba9116 docs: add doc set_zh-CN.md 2022-06-06 11:20:55 +08:00
dudaodong
56d1e5e95c docs: fix code format 2022-06-06 11:12:53 +08:00
dudaodong
11d86d0270 docs: add doc for Set datastructer 2022-06-06 11:07:43 +08:00
dudaodong
d1c20a1da8 fix: fix function comment 2022-06-06 11:07:13 +08:00
dudaodong
f87ab89207 docs: fix misspell in doc queue.md 2022-06-06 10:34:18 +08:00
dudaodong
9ebb0c7920 docs: add doc queue_zh-CN.md 2022-06-06 10:30:26 +08:00
dudaodong
31a5ed11a3 docs: add doc for PriorityQueue 2022-06-05 17:32:37 +08:00
dudaodong
a706d488e6 feat: add Size func for PriorityQueue 2022-06-05 17:29:36 +08:00
dudaodong
61251fb0f1 refactor: change func Length to Size in CircularQueue 2022-06-05 16:57:03 +08:00
dudaodong
747dc30b02 refactor: Change param __TEXT __DATA __OBJC others dec hex to in NewCircularQueue function 2022-06-05 16:49:30 +08:00
dudaodong
d54f27d9a9 docs: add IsFull function for ArrayQueue 2022-06-05 16:46:01 +08:00
dudaodong
dbca2f6be4 feat: add IsFull function for ArrayQueue 2022-06-05 16:43:15 +08:00
dudaodong
2ef9b56d22 feat: add Contain function for LinkedQueue 2022-06-05 16:41:16 +08:00
dudaodong
52ecde7e24 docs: add doc for LinkedQueue 2022-06-05 16:36:56 +08:00
dudaodong
a7dce5e4d3 docs: add doc for LinkedQueue 2022-06-04 18:57:43 +08:00
dudaodong
4718da44f4 refactor: rename function 'EnQueue' -> 'Enqueue', 'DeQueue' -> 'Dequeue' 2022-06-04 18:53:46 +08:00
dudaodong
1aa1f95524 docs: add doc for queue 2022-06-04 18:49:24 +08:00
dudaodong
d922b2e778 docs: fix code format 2022-06-04 11:26:39 +08:00
dudaodong
5a7b3c4a37 docs: add stack_zh-CN.md doc 2022-06-03 19:20:14 +08:00
dudaodong
1c2b1a2f02 docs: update Peak function comment 2022-06-02 22:26:26 +08:00
dudaodong
c68440ecf8 docs: add docs for stack 2022-06-02 22:25:11 +08:00
dudaodong
6a48b3f99e docs: add docs for linklist 2022-06-01 20:48:30 +08:00
dudaodong
bab9ae32d4 docs: add docs for package datastructure DoublyLink 2022-06-01 20:30:30 +08:00
dudaodong
f0e6e94c5e fix: fix incorrect comment for DoublyLink Clear function 2022-06-01 20:16:34 +08:00
dudaodong
a6fe155781 docs: fix code format 2022-05-31 21:47:56 +08:00
dudaodong
d5be9009fc docs: update list doc 2022-05-31 21:43:26 +08:00
dudaodong
ca47be41f6 fix: fix incorrect comment for SinglyLink Clear function 2022-05-31 21:42:31 +08:00
dudaodong
ff5db30208 docs: add docs for package datastructure SinglyLink 2022-05-31 21:41:25 +08:00
dudaodong
a429d46072 docs: add list_zh-CN.md 2022-05-30 21:09:21 +08:00
dudaodong
7b84fbfd94 fix: fix misspellings -> 2022-05-30 20:54:34 +08:00
dudaodong
fc6f618885 docs: add docs for package datastructure list 2022-05-30 20:53:11 +08:00
dudaodong
7f03c3b0a2 docs: update algorithm package doc 2022-05-30 17:53:21 +08:00
dudaodong
4b4386bd47 Merge branch 'v2' of github.com:duke-git/lancet into v2 2022-05-28 10:54:55 +08:00
dudaodong
10a1706613 docs: update function comments 2022-05-28 10:51:15 +08:00
dudaodong
d097f356fd docs: change some function comments for slice doc 2022-05-21 17:00:28 +08:00
dudaodong
bc9bacaa55 refactor: change param to item 2022-05-21 16:50:57 +08:00
dudaodong
c949059fb1 release: v2.0.7 2022-05-18 15:29:55 +08:00
dudaodong
9c6d489db7 docs: add some new funcs documentation 2022-05-18 15:28:18 +08:00
dudaodong
45436798af fix: fix TestMinBy test function 2022-05-18 15:23:57 +08:00
dudaodong
bd0eb0682c docs: add doc for func MaxBy and MinBy 2022-05-18 15:22:44 +08:00
dudaodong
bdbc06b095 docs: add doc for funcs IndexOf and LastIndexOf 2022-05-18 15:00:09 +08:00
dudaodong
2f504ce851 test: add unit test for func LastIndexOf 2022-05-16 11:11:40 +08:00
dudaodong
02a24b461c test: add unit test for func LastIndexOf 2022-05-16 11:05:52 +08:00
dudaodong
3f7a81c005 test: add unit test for func IndexOf 2022-05-16 11:04:40 +08:00
dudaodong
cc6e10ee5a add func IndexOf and LastIndexOf 2022-05-16 10:40:33 +08:00
dudaodong
eff2f22440 test: add unit test for MinBy function 2022-05-15 18:28:40 +08:00
dudaodong
47b6747bb0 feat: add MinBy function 2022-05-15 18:25:46 +08:00
dudaodong
298219aee7 test: add unit test for MaxBy function 2022-05-15 18:21:01 +08:00
dudaodong
d5607b0a5a feat: add MaxBy function 2022-05-15 18:15:05 +08:00
IHIDCHAOS
f47873fbc7 Update system_zh-CN.md (#32)
fix typo
2022-05-15 17:28:37 +08:00
dudaodong
f24ad26692 release: v2.0.6 2022-05-12 10:13:13 +08:00
dudaodong
88de9bfac2 docs: add doc for SplitEx function 2022-05-12 10:12:19 +08:00
dudaodong
e4777a0986 docs: add doc for SplitEx function 2022-05-12 10:11:12 +08:00
franktz
84a69d1fa5 feat: strutil.SplitEx. Split a given string whether the result contains empty string (#31)
Co-authored-by: Frank.Town <tangzhi21@sfmail.sf-express.com>
2022-05-11 15:19:53 +08:00
dudaodong
5eb056277d update readme file 2022-04-27 11:51:02 +08:00
dudaodong
4e6d586251 update readme file 2022-04-27 11:42:06 +08:00
dudaodong
61c7012e17 test: add unit test TestBSTree_IsSubTree 2022-04-27 11:36:51 +08:00
dudaodong
0d48778886 feat: add HasSubTree func for BSTree 2022-04-27 10:43:01 +08:00
dudaodong
e98c46c903 feat: add IsSubTree func for BSTree 2022-04-27 10:36:11 +08:00
dudaodong
155f287ab0 refactor: add param comparator in NewBSTree func 2022-04-26 11:04:04 +08:00
dudaodong
652916b7d7 test: add unit test for PriorityQueue Dequeue function 2022-04-26 10:56:24 +08:00
dudaodong
955de2bdbf feat: add Dequeue function 2022-04-26 10:50:27 +08:00
dudaodong
a4c1d40faa test: add unit test for PriorityQueue Enqueue function 2022-04-26 10:45:19 +08:00
dudaodong
f155e0caa6 test: add unit test for PriorityQueue Enqueue function 2022-04-26 10:40:48 +08:00
dudaodong
fb0332449c feat: add IsEmpty and IsFull for PriorityQueue 2022-04-26 10:24:10 +08:00
dudaodong
68f0fd1d4c feat: add PriorityQueue 2022-04-26 10:19:31 +08:00
dudaodong
70995c5098 feat: add PriorityQueue 2022-04-26 10:18:43 +08:00
dudaodong
55e62ed8ca feat: add PriorityQueue 2022-04-26 10:18:06 +08:00
dudaodong
e614274f07 release v2.0.5 2022-04-24 10:34:25 +08:00
dudaodong
c524eb04a1 docs: add doc for concurrency in readme file 2022-04-24 10:32:55 +08:00
dudaodong
985b3cddd8 fix: fix TestTee func 2022-04-24 10:26:07 +08:00
dudaodong
abadeec007 docs: add doc for channel.go 2022-04-24 10:17:34 +08:00
dudaodong
3ab05154aa docs: add doc for channel.go 2022-04-22 17:12:41 +08:00
dudaodong
9f1c89bf0e refactor: refact FanIn func in channel.go 2022-04-21 14:31:43 +08:00
dudaodong
9444582e44 fix: fix OrDone func 2022-04-21 14:19:38 +08:00
dudaodong
c27ccad2b9 refactor: refact Or func in channel.go 2022-04-21 14:13:19 +08:00
dudaodong
d1c6c57700 feat: add func Bridge 2022-04-21 11:26:02 +08:00
dudaodong
922999037f feat: add func Tee 2022-04-21 11:18:45 +08:00
dudaodong
046e90486d feat: add func in channel.go 2022-04-19 16:21:23 +08:00
dudaodong
fc6dee9e77 feat: add func in channel.go 2022-04-19 16:03:12 +08:00
dudaodong
980ff2c363 feat: add FanIn for channel 2022-04-19 15:34:26 +08:00
dudaodong
763aa8e10d refactor: replace param done channel with ctx context 2022-04-19 14:42:55 +08:00
dudaodong
83c0d1d6e6 fix: fix misspellings 2022-04-15 17:02:34 +08:00
dudaodong
dfc6b868fb feat: add RepeateFn function 2022-04-15 16:32:55 +08:00
dudaodong
f66c0938e5 feat: add Take function to generate a chan of values taken from another chan 2022-04-15 16:22:19 +08:00
dudaodong
a4900fecb4 feat: add Repeate function to generate a chan of repeate values 2022-04-15 16:08:14 +08:00
dudaodong
dc25bdab2f feat: add Generate chan function in concurrency package 2022-04-15 15:56:06 +08:00
dudaodong
bb23c9eef8 release v2.0.4 2022-04-14 11:51:34 +08:00
dudaodong
b4cd0750e4 fix: fix http post to support multipart/form-data header 2022-04-14 11:48:39 +08:00
dudaodong
c960841491 docs: update sort function doc 2022-04-12 14:45:22 +08:00
dudaodong
5d6f9443fd refactor: rewrite sort function 2022-04-12 14:30:43 +08:00
dudaodong
5483380066 refactor: rewrite arrayqueue 2022-04-08 17:39:54 +08:00
dudaodong
20b9e5353e fix: fix goreport issue 2022-04-06 11:33:48 +08:00
dudaodong
1ec3a5af87 release v2.0.3 2022-04-06 10:54:54 +08:00
dudaodong
f31dde97b1 docs: add maputil docs in read me file 2022-04-05 14:24:33 +08:00
dudaodong
1718fd1cf1 docs: add doc for maputil package 2022-04-05 14:16:58 +08:00
dudaodong
513c0f829c docs: add doc for SymmetricDifference fun in slice doc 2022-04-05 13:47:13 +08:00
dudaodong
c51a806aff feat: add SymmetricDifference func for set 2022-04-01 18:06:34 +08:00
dudaodong
3c16d50c4b refactor: rename ReverseIntersect to SymmetricDifference 2022-04-01 18:05:02 +08:00
dudaodong
21dd6ab8aa feat: add SymmetricDifference func for set 2022-04-01 18:02:12 +08:00
dudaodong
f5bf5183cc feat: add Minus func for set 2022-04-01 16:48:37 +08:00
dudaodong
f28b5b2f92 feat: add Minus func for set 2022-04-01 16:44:25 +08:00
dudaodong
79867e8a63 feat: add ReverseIntersect function 2022-03-31 19:42:27 +08:00
dudaodong
19939c2b03 feat: add Minus func 2022-03-31 18:00:11 +08:00
dudaodong
bf7ffbfa8d feat: add Intersect func 2022-03-31 16:56:12 +08:00
dudaodong
cbb46f9cb4 feat: add Filter func 2022-03-30 19:52:43 +08:00
dudaodong
3ad142d5d7 feat: add ForEach func 2022-03-30 19:41:55 +08:00
dudaodong
1a436aeb41 feat: add Merge func 2022-03-30 18:06:22 +08:00
dudaodong
1af8fe8daf test: add unit test for Keys and Values func 2022-03-30 17:45:17 +08:00
dudaodong
ae54c8db6f feat: add Values func 2022-03-30 17:34:01 +08:00
dudaodong
be942ec33e feat: add package maputil and Keys func 2022-03-30 17:27:50 +08:00
dudaodong
fafb59dad6 update readme file 2022-03-29 11:02:27 +08:00
dudaodong
e36c01cac9 release v2.0.2 2022-03-29 11:01:31 +08:00
dudaodong
aa0afa8d94 release v1.2.7 2022-03-29 11:01:06 +08:00
dudaodong
18e0031e0e fix: fix TestInsertionSort 2022-03-26 21:06:05 +08:00
dudaodong
a5018c110c fix: fix bug in ToBytes func 2022-03-26 20:54:37 +08:00
dudaodong
142deb83b2 fix: fix InsertionSort bug 2022-03-26 18:25:42 +08:00
dudaodong
ccc0188352 feat: add DeleteValue for SinglyLink 2022-03-24 17:56:45 +08:00
dudaodong
98dba83107 release v2.0.1 2022-03-22 14:47:23 +08:00
dudaodong
9c6aff9030 fix: fix lint warning 'ineffassign' 2022-03-22 14:45:16 +08:00
dudaodong
7fd9a94922 fix: fix lint warning 'ineffassign' 2022-03-22 14:36:35 +08:00
dudaodong
dd3b1f3aed docs: add doc for some new funcs 2022-03-22 14:10:58 +08:00
dudaodong
f9dce592e8 docs: add doc for Min, Max, and Average 2022-03-22 10:43:00 +08:00
dudaodong
c4b0967623 feat: add Max, Min, and Average function 2022-03-22 10:26:43 +08:00
dudaodong
41613061d5 docs: add GroupWith doc 2022-03-21 20:10:39 +08:00
dudaodong
50ef63e27d feat: add GroupWith func 2022-03-21 15:29:10 +08:00
dudaodong
3ae4a35d04 fix: format timezone issue 2022-03-19 19:24:28 +08:00
dudaodong
89ea0ee15a fix: format timezone issue 2022-03-19 19:08:34 +08:00
dudaodong
85399e23ed fix: format timezone issue 2022-03-19 18:42:22 +08:00
dudaodong
19a6f218e6 fix: format time in local timezone 2022-03-19 18:23:06 +08:00
dudaodong
ccaf290b63 docs: add doc for datetime/conversion.go 2022-03-18 18:18:28 +08:00
dudaodong
54745d512c test: add unit test and func comment for time conversion 2022-03-18 17:48:57 +08:00
dudaodong
39234d4722 Merge branch 'main' of github.com:duke-git/lancet into main 2022-03-17 19:46:31 +08:00
dudaodong
4b4310265b fix: update TestListFileNames 2022-03-17 19:46:19 +08:00
wangxudong123
d8a982bf07 增加时间转换 (#25)
Co-authored-by: wangxd01 <wangxd01@mingyuanyun.com>
2022-03-17 19:44:24 +08:00
dudaodong
0334a6f02b docs: fix some issues 2022-03-17 11:55:52 +08:00
dudaodong
ae0c613b8e add package comment 2022-03-17 11:55:08 +08:00
dudaodong
40b2560752 docs: add v2 in import path 2022-03-17 11:33:45 +08:00
dudaodong
50c6e51393 upgrade go module v2 2022-03-17 10:57:35 +08:00
dudaodong
1434e00712 docs: add func HeapSort 2022-03-17 10:11:34 +08:00
dudaodong
76c941cff0 update readme file 2022-03-16 19:39:21 +08:00
dudaodong
bb03f31a8b release v2.0.0 2022-03-16 19:16:43 +08:00
dudaodong
c939b26cb8 refactor: interface{} -> any 2022-03-16 18:41:40 +08:00
dudaodong
af480efa8c docs: add xerror package doc 2022-03-16 18:21:30 +08:00
dudaodong
bc913d70b1 docs: update doc style 2022-03-16 18:06:06 +08:00
dudaodong
2bc87d8a6b docs: add docs/algorithm.md 2022-03-16 18:00:38 +08:00
dudaodong
2185c48eab docs: add docs/algorithm.md 2022-03-16 18:00:29 +08:00
dudaodong
59b5046a15 docs: update slice.md 2022-03-16 16:59:15 +08:00
dudaodong
569902b528 fix: fix lru cache bug 2022-03-14 11:19:30 +08:00
dudaodong
78519088ab feat: add lru cache 2022-03-11 17:05:55 +08:00
dudaodong
0c06cdb29f Merge branch 'main' into v2 2022-03-04 17:42:18 +08:00
dudaodong
e8b8ff8927 merge main branch 2022-03-01 15:00:02 +08:00
dudaodong
06bd407a0c feat: add LevelOrderTraverse func 2022-03-01 11:23:33 +08:00
dudaodong
aa28479d11 feat: update deleteTreeNode func 2022-03-01 11:13:44 +08:00
dudaodong
bf7db0ded2 feat: add DeletetNode 2022-03-01 11:04:38 +08:00
dudaodong
dd1cbf2ee3 refactor: rename Insert to InsertNode 2022-03-01 10:11:37 +08:00
dudaodong
9d5db895c9 Merge branch 'main' into v2 2022-03-01 10:02:23 +08:00
dudaodong
6b09da6e6e feat: add funcs to traverse bstree 2022-02-23 19:34:35 +08:00
dudaodong
b2b6710a1b feat: add BSTree which is binary search tree 2022-02-21 16:48:27 +08:00
dudaodong
e7ee2ed7cf Merge branch 'main' into v2 2022-02-20 11:35:43 +08:00
dudaodong
1dfd1ec1d3 Merge branch 'main' into v2 2022-02-19 21:55:09 +08:00
dudaodong
2e8834a2c1 docs: update slice.doc 2022-02-18 11:05:14 +08:00
dudaodong
df098392c2 merge main branch 2022-02-18 10:28:47 +08:00
dudaodong
5e318a78d2 refactor: remove unused code 2022-02-15 11:19:07 +08:00
dudaodong
d872d64fe0 feat: add CircularQueue 2022-02-15 10:34:14 +08:00
dudaodong
c2257493a8 fix: fix panic info text 2022-02-14 15:36:44 +08:00
dudaodong
48c1f8ffad fix: fix some spell mistake 2022-02-13 19:00:44 +08:00
dudaodong
8a4c8218d2 update func DeQueue comment 2022-02-11 10:49:33 +08:00
dudaodong
92e1321d43 refactor: rename func Length -> Size 2022-02-11 10:48:38 +08:00
dudaodong
43fb907a81 feat: add LinkedQueue 2022-02-11 10:46:48 +08:00
dudaodong
f551c56921 refactor: reconstructure souce file into the corresponding folder 2022-02-10 17:57:53 +08:00
dudaodong
85e1f711f5 feat: and Clear and IsEmpty func 2022-02-10 17:41:51 +08:00
dudaodong
ade567a620 feat: add ArrayQueue data structure 2022-02-10 17:30:37 +08:00
dudaodong
a26e519a3f refactor: rename func Length -> Size 2022-02-10 15:42:05 +08:00
dudaodong
797e47363f rename: rename StackArray -> ArrayStack, StackLink -> LinkedStack 2022-02-10 11:50:03 +08:00
dudaodong
20e1836eb7 rename: rename StackArray -> ArrayStack, StackLink -> LinkedStack 2022-02-10 11:49:46 +08:00
dudaodong
5ae746a1f0 rename: rename InsertByIndex -> InsertAt, UpdateByIndex -> UpdateAt, DeleteByIndex -> DeleteAt for release 2.0 2022-02-10 11:25:34 +08:00
dudaodong
17d271190b feat: add DoublyLink data structure 2022-02-10 10:57:17 +08:00
dudaodong
310f8bd1e1 feat: add Set data structure 2022-02-09 15:15:49 +08:00
dudaodong
bf7a4e729f feat: add func Contain, Union, Intersection 2022-02-09 11:40:08 +08:00
dudaodong
8d0ff28304 update comment for StackLink 2022-02-04 22:45:01 +08:00
dudaodong
cee9bb538f feat: add stacklink.go implements stack with link 2022-02-04 22:42:51 +08:00
dudaodong
eb59d02a08 feat: add stackarray.go implements stack with array 2022-02-04 19:35:06 +08:00
dudaodong
10ed71a3a2 refactor: rename linknode.go to node.go 2022-02-04 09:22:16 +08:00
dudaodong
0e86244559 refactor: rename linknode.go to node.go 2022-02-04 09:21:35 +08:00
dudaodong
5ab150cad3 feat: add DeleteAtHead, DeleteAtTail, DeleteAt, Reverse, Size, GetMiddleNode funcs in singlelink 2022-02-03 16:37:34 +08:00
dudaodong
3546afe86c feat: add some functons of singlelink 2022-02-02 22:41:49 +08:00
dudaodong
70077a6010 merge main branch 2022-01-26 17:47:57 +08:00
dudaodong
efdf36a817 Merge branch 'main' into v2 2022-01-26 17:44:46 +08:00
dudaodong
e233788f69 comment: add comment for List struct 2022-01-26 15:25:52 +08:00
dudaodong
957e6356f6 feat: add singlelink 2022-01-26 15:25:02 +08:00
dudaodong
887eaa528a update: algorithm package comment 2022-01-26 14:57:37 +08:00
dudaodong
33126570bd feat: add datastructure package and list implementation 2022-01-26 14:56:40 +08:00
dudaodong
5937183af0 feat: replace constraints comparable into any in some funcs 2022-01-24 15:47:47 +08:00
dudaodong
4eaa0a39d5 feat: add FindLast func 2022-01-24 15:22:43 +08:00
dudaodong
18ec73839b feat: add DifferenceWith 2022-01-24 14:53:45 +08:00
dudaodong
89aef977a2 refactor: clean code for slice/slice.go 2022-01-24 11:46:09 +08:00
dudaodong
2612569500 merge: merge main branch and refector slice func with generics 2022-01-24 11:10:12 +08:00
dudaodong
acc8b59387 comment: add comment for Unwrap 2022-01-24 10:24:10 +08:00
donutloop
2878d389c8 error: Add unwrap (#24)
Add a unwrap func helper

* Unwrap if err is nil then it returns a valid value otherwise it panics
2022-01-22 21:25:31 +08:00
dudaodong
3f8effb7a3 refactor: make BinarySearch func code clear 2022-01-21 11:57:36 +08:00
dudaodong
bd30855ae6 feat: add LinearSearch 2022-01-21 11:36:31 +08:00
dudaodong
0efe2f57c3 merge main 2022-01-19 20:47:47 +08:00
dudaodong
8868fcabed Merge branch 'main' into v2 2022-01-19 17:35:48 +08:00
dudaodong
dfbb9e30e0 feat: algorithm package, BinarySearch and BinaryIterativeSearch functions 2022-01-17 15:36:48 +08:00
dudaodong
b22be7cade refactor: change var name low -> lowIndex, high -> highIndex 2022-01-17 15:11:33 +08:00
dudaodong
87da6c6816 Merge branch 'main' into v2 2022-01-17 15:01:38 +08:00
dudaodong
912f7052a3 gofmt 2022-01-17 10:29:42 +08:00
dudaodong
e6f9b0954c feat: add CountSort 2022-01-16 17:20:20 +08:00
dudaodong
98e861cf3b feat: add QuickSort, HeapSort, and MergeSort functions 2022-01-16 16:52:17 +08:00
dudaodong
43e0ca7edf feat: add ShellSort 2022-01-14 18:17:46 +08:00
dudaodong
d491bea263 feat: add BubbleSort and InsertionSort 2022-01-14 17:57:14 +08:00
dudaodong
6f1feb96d6 experimental feature, algorithm/sorter.go try to implements sort function with go generics 2022-01-14 17:01:44 +08:00
dudaodong
d46d12f949 feat: add func ContainSubSlice 2022-01-13 11:52:21 +08:00
dudaodong
c6fc92a94c update go version to v1.18 2022-01-12 10:23:04 +08:00
dudaodong
f4fa790b72 Merge branch 'main' into v2 2022-01-12 10:05:35 +08:00
dudaodong
22798fd750 fix: fix some go report issue 2022-01-12 10:01:48 +08:00
dudaodong
907df56f86 update: make func comment consistent 2022-01-11 20:27:06 +08:00
dudaodong
3dadfa234b update: make func comment consistent 2022-01-11 19:33:15 +08:00
donutloop
f1d7154179 refactor: sort (#21)
Removed reverse func because it use to much cpu and add test cas for asc sort
2022-01-11 19:24:36 +08:00
dudaodong
dc47b9ce98 fix: fix confict in readme file 2022-01-11 18:15:09 +08:00
dudaodong
6e626851cf merge #pr20 2022-01-11 18:12:57 +08:00
donutloop
c906d8aea7 Slice: group by and without v2 (#20)
* Slice: group by and without v2

Migrate from reflection to generic

* update doc
2022-01-11 18:11:01 +08:00
dudaodong
e5e4e09308 comment without func 2022-01-11 18:10:21 +08:00
dudaodong
7ed173849a refactor: rewrite DeleteByIndex, InsertByIndex, UpdateByIndex and Without func with generics 2022-01-11 18:08:22 +08:00
donutloop
24c0d95112 Slice: reverse and shufffle v2 (#19)
* Slice: reverse and shufffle v2

Migrate from reflection to generic form

Note: after migration, old reverse must be removed

* Suffle test: remove reflection calls

* Reverse test: change name of test
2022-01-10 19:52:23 +08:00
dudaodong
e9fed34729 fmt: slice.go 2022-01-10 10:27:20 +08:00
dudaodong
9caefdb67d fmt: slice.go 2022-01-10 10:24:33 +08:00
dudaodong
681d5b6bdf update comment for Count func 2022-01-09 22:17:50 +08:00
dudaodong
b0e9758e0d Merge branch 'v2' of github.com:duke-git/lancet into v2 2022-01-09 22:15:18 +08:00
donutloop
7b9a8a55e7 Slice: CountV2 (#18)
Use generic instead of reflection
2022-01-09 22:15:03 +08:00
dudaodong
44ac82e8b8 refactor: rewrite some slice functions with go generics 2022-01-09 22:14:46 +08:00
dudaodong
c4b4cb1173 merge master: rewrite all unit test functions with assert 2022-01-09 18:33:00 +08:00
donutloop
30d798366b Slice: find v2 (#17)
* If slice len is zero then return false
* Convert find to generic form.
* If return T is nil then no items matched the predicate func
2022-01-07 20:19:27 +08:00
dudaodong
3e9a2b5c59 refactor: refact Filter, ForEach, Map func with generics 2022-01-07 17:53:55 +08:00
dudaodong
1343683b08 checkout v2 branch for generics migration 2022-01-07 17:48:00 +08:00
288 changed files with 88303 additions and 15956 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
liberapay: Duke_Du
patreon: DukeDu

View File

@@ -17,7 +17,7 @@ jobs:
fetch-depth: 2
- uses: actions/setup-go@v2
with:
go-version: "1.16"
go-version: "1.18"
- name: Run coverage
run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic
- name: Upload coverage to Codecov

8
.gitignore vendored
View File

@@ -6,4 +6,10 @@ fileutil/*.txt
fileutil/*.zip
fileutil/*.link
fileutil/unzip/*
cryptor/*.pem
fileutil/tempdir/*
slice/testdata/*
cryptor/*.pem
test
docs/node_modules
docs/.vitepress/cache
docs/.vitepress/dist

37
CONTRIBUTING.en-US.md Normal file
View File

@@ -0,0 +1,37 @@
# Lancet Contributing Guide
Hi! Thank you for choosing Lancet.
Lancet is a powerful, efficient, and reusable util function library of go. It makes Go dev easier by taking the hassle out of working with concurrency, net, math, slice, string, etc.
We are excited that you are interested in contributing to lancet. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines.
## Issue Guidelines
- Issues are exclusively for bug reports, feature requests and design-related topics. Other questions may be closed directly.
- Before submitting an issue, please check if similar problems have already been issued.
- Please specify which version of Lancet and Go you are using, and provide OS information. [Go Playground](https://go.dev/play/) is recommended to build a live demo so that your issue can be reproduced clearly.
## Pull Request Guidelines
- Fork this repository to your own account. Do not create branches here.
- Commit info should be formatted as `type(scope): info about commit`. eg. `fix(package): [scrollbar] fix xxx bug`.
1. type: type must be one of [chore, docs, feat, fix, refactor, release, test].
2. scope: scope must be one of [package, file, internal].
3. header: header must not be longer than 72 characters.
- Rebase before creating a PR to keep commit history clear.
- 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.
- If your PR fixes a bug, please provide a description about the related bug.
- If the PR is for a new feature, make sure to complete the relevant documentation (/lancet/docs/en/api/packages).

37
CONTRIBUTING.zh-CN.md Normal file
View File

@@ -0,0 +1,37 @@
# Lancet 贡献指南
Hi! 首先感谢你使用 Lancet。
lancet柳叶刀是一个功能强大、全面、高效、可复用的go语言工具函数库。它消除了处理并发、网络、数学、切片、字符串等的麻烦使 Go 开发变得更容易。
Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代码或提供建议,请阅读以下内容。
## Issue 规范
- issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。
- 在提交 issue 之前,请搜索相关内容是否已被提出。
- 请说明 Lancet 和 Go 的版本号,并提供操作系统信息。推荐使用 [Go Playground](https://go.dev/play/) 生成在线 demo这能够更直观地重现问题。
## Pull Request 规范
- 请先 fork 一份到自己的项目下,不要直接在仓库下建分支。
- commit 信息要以 `type(scope): 描述信息` 的形式填写,例如 `fix(package): [scrollbar] fix xxx bug`
1. type: 必须是 chore, docs, feat, fix, refactor, release, test 其中的一个。
2. scope: 必须是 package, file, internal 其中的一个。
3. header: 描述信息不要超过 72 个字符。
- 提交 PR 前请 rebase确保 commit 记录的整洁。
- 提交 PR 前请执行单元测试命令go test -v ./...,确保所有单元测试任务通过。
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
- 如果是修复 bug请在 PR 中给出描述信息。
- 如果PR是新功能确保完成相关文档(/lancet/docs/api/packages)。

2110
README.md

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

15
SECURITY.md Normal file
View File

@@ -0,0 +1,15 @@
# Security Policy
## Supported Versions
Here is the lancet version and compatibility with go language version.
| Version | Supported |
| ------- | ------------------|
| 2.x.x | +go v1.18 |
| 1.x.x | +go v1.12 |
## Reporting a Vulnerability
For now, there is no public website to report a vulnerability, If you find security issue in lancet, you can send it to me via my email `lanliddd.2007@163.com`.
we can discuss it. I am appreciate if someone can create a public page for reporting vulnerability.

121
algorithm/lrucache.go Normal file
View File

@@ -0,0 +1,121 @@
package algorithm
type lruNode[K comparable, V any] struct {
key K
value V
pre *lruNode[K, V]
next *lruNode[K, V]
}
// newLruNode return a lruNode pointer
func newLruNode[K comparable, V any](key K, value V) *lruNode[K, V] {
return &lruNode[K, V]{
key: key,
value: value,
pre: nil,
next: nil,
}
}
// LRUCache lru cache (thread unsafe)
type LRUCache[K comparable, V any] struct {
cache map[K]*lruNode[K, V]
head *lruNode[K, V]
tail *lruNode[K, V]
capacity int
length int
}
// NewLRUCache creates a LRUCache pointer instance.
func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] {
return &LRUCache[K, V]{
cache: make(map[K]*lruNode[K, V], capacity),
head: nil,
tail: nil,
capacity: capacity,
length: 0,
}
}
// Get value of key from lru cache.
// Play: https://go.dev/play/p/iUynEfOP8G0
func (l *LRUCache[K, V]) Get(key K) (V, bool) {
var value V
node, ok := l.cache[key]
if ok {
l.moveToHead(node)
return node.value, true
}
return value, false
}
// Put value of key into lru cache.
// Play: https://go.dev/play/p/iUynEfOP8G0
func (l *LRUCache[K, V]) Put(key K, value V) {
node, ok := l.cache[key]
if !ok {
newNode := newLruNode(key, value)
l.cache[key] = newNode
l.addNode(newNode)
if len(l.cache) > l.capacity {
oldKey := l.deleteNode(l.head)
delete(l.cache, oldKey)
}
} else {
node.value = value
l.moveToHead(node)
}
l.length = len(l.cache)
}
// Delete item from lru cache.
func (l *LRUCache[K, V]) Delete(key K) bool {
node, ok := l.cache[key]
if ok {
key := l.deleteNode(node)
delete(l.cache, key)
return true
}
return false
}
// Len returns the number of items in the cache.
func (l *LRUCache[K, V]) Len() int {
return l.length
}
func (l *LRUCache[K, V]) addNode(node *lruNode[K, V]) {
if l.tail != nil {
l.tail.next = node
node.pre = l.tail
node.next = nil
}
l.tail = node
if l.head == nil {
l.head = node
}
}
func (l *LRUCache[K, V]) deleteNode(node *lruNode[K, V]) K {
if node == l.tail {
l.tail = l.tail.pre
} else if node == l.head {
l.head = l.head.next
} else {
node.pre.next = node.next
node.next.pre = node.pre
}
return node.key
}
func (l *LRUCache[K, V]) moveToHead(node *lruNode[K, V]) {
if l.tail == node {
return
}
l.deleteNode(node)
l.addNode(node)
}

View File

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

View File

@@ -0,0 +1,34 @@
package algorithm
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestLRUCache(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestLRUCache")
cache := NewLRUCache[int, int](3)
cache.Put(1, 1)
cache.Put(2, 2)
cache.Put(3, 3)
asssert.Equal(3, cache.Len())
v, ok := cache.Get(1)
asssert.Equal(true, ok)
asssert.Equal(1, v)
v, ok = cache.Get(2)
asssert.Equal(true, ok)
asssert.Equal(2, v)
ok = cache.Delete(2)
asssert.Equal(true, ok)
_, ok = cache.Get(2)
asssert.Equal(false, ok)
}

66
algorithm/search.go Normal file
View File

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

View File

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

41
algorithm/search_test.go Normal file
View File

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

199
algorithm/sort.go Normal file
View File

@@ -0,0 +1,199 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
package algorithm
import "github.com/duke-git/lancet/v2/lancetconstraints"
// BubbleSort applys the bubble sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/GNdv7Jg2Taj
func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator) {
for i := 0; i < len(slice); i++ {
for j := 0; j < len(slice)-1-i; j++ {
isCurrGreatThanNext := comparator.Compare(slice[j], slice[j+1]) == 1
if isCurrGreatThanNext {
swap(slice, j, j+1)
}
}
}
}
// InsertionSort applys the insertion sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/G5LJiWgJJW6
func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
for i := 0; i < len(slice); i++ {
for j := i; j > 0; j-- {
isPreLessThanCurrent := comparator.Compare(slice[j], slice[j-1]) == -1
if isPreLessThanCurrent {
swap(slice, j, j-1)
} else {
break
}
}
}
}
// SelectionSort applys the selection sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/oXovbkekayS
func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
for i := 0; i < len(slice); i++ {
min := i
for j := i + 1; j < len(slice); j++ {
if comparator.Compare(slice[j], slice[min]) == -1 {
min = j
}
}
swap(slice, i, min)
}
}
// ShellSort applys the shell sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/3ibkszpJEu3
func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) {
size := len(slice)
gap := 1
for gap < size/3 {
gap = 3*gap + 1
}
for gap >= 1 {
for i := gap; i < size; i++ {
for j := i; j >= gap && comparator.Compare(slice[j], slice[j-gap]) == -1; j -= gap {
swap(slice, j, j-gap)
}
}
gap = gap / 3
}
}
// QuickSort quick sorting for slice, lowIndex is 0 and highIndex is len(slice)-1.
// Play: https://go.dev/play/p/7Y7c1Elk3ax
func QuickSort[T any](slice []T, comparator lancetconstraints.Comparator) {
quickSort(slice, 0, len(slice)-1, comparator)
}
func quickSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) {
if lowIndex < highIndex {
p := partition(slice, lowIndex, highIndex, comparator)
quickSort(slice, lowIndex, p-1, comparator)
quickSort(slice, p+1, highIndex, comparator)
}
}
// partition split slice into two parts
func partition[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int {
p := slice[highIndex]
i := lowIndex
for j := lowIndex; j < highIndex; j++ {
if comparator.Compare(slice[j], p) == -1 { //slice[j] < p
swap(slice, i, j)
i++
}
}
swap(slice, i, highIndex)
return i
}
// HeapSort applys the heap sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/u6Iwa1VZS_f
func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator) {
size := len(slice)
for i := size/2 - 1; i >= 0; i-- {
sift(slice, i, size-1, comparator)
}
for j := size - 1; j > 0; j-- {
swap(slice, 0, j)
sift(slice, 0, j-1, comparator)
}
}
func sift[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) {
i := lowIndex
j := 2*i + 1
temp := slice[i]
for j <= highIndex {
if j < highIndex && comparator.Compare(slice[j], slice[j+1]) == -1 { //slice[j] < slice[j+1]
j++
}
if comparator.Compare(temp, slice[j]) == -1 { //tmp < slice[j]
slice[i] = slice[j]
i = j
j = 2*i + 1
} else {
break
}
}
slice[i] = temp
}
// MergeSort applys the merge sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/ydinn9YzUJn
func MergeSort[T any](slice []T, comparator lancetconstraints.Comparator) {
mergeSort(slice, 0, len(slice)-1, comparator)
}
func mergeSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) {
if lowIndex < highIndex {
mid := (lowIndex + highIndex) / 2
mergeSort(slice, lowIndex, mid, comparator)
mergeSort(slice, mid+1, highIndex, comparator)
merge(slice, lowIndex, mid, highIndex, comparator)
}
}
func merge[T any](slice []T, lowIndex, midIndex, highIndex int, comparator lancetconstraints.Comparator) {
i := lowIndex
j := midIndex + 1
temp := []T{}
for i <= midIndex && j <= highIndex {
//slice[i] < slice[j]
if comparator.Compare(slice[i], slice[j]) == -1 {
temp = append(temp, slice[i])
i++
} else {
temp = append(temp, slice[j])
j++
}
}
if i <= midIndex {
temp = append(temp, slice[i:midIndex+1]...)
} else {
temp = append(temp, slice[j:highIndex+1]...)
}
for k := 0; k < len(temp); k++ {
slice[lowIndex+k] = temp[k]
}
}
// CountSort applys the count sort algorithm to sort the collection, don't change the original collection data.
// Play: https://go.dev/play/p/tB-Umgm0DrP
func CountSort[T any](slice []T, comparator lancetconstraints.Comparator) []T {
size := len(slice)
out := make([]T, size)
for i := 0; i < size; i++ {
count := 0
for j := 0; j < size; j++ {
//slice[i] > slice[j]
if comparator.Compare(slice[i], slice[j]) == 1 {
count++
}
}
out[count] = slice[i]
}
return out
}
// swap two slice value at index i and j
func swap[T any](slice []T, i, j int) {
slice[i], slice[j] = slice[j], slice[i]
}

View File

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

217
algorithm/sort_test.go Normal file
View File

@@ -0,0 +1,217 @@
package algorithm
import (
"fmt"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
// People test mock data
type people struct {
Name string
Age int
}
// PeopleAageComparator sort people slice by age field
type peopleAgeComparator struct{}
// Compare implements github.com/duke-git/lancet/v2/lancetconstraints/constraints.go/Comparator
func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int {
p1, _ := v1.(people)
p2, _ := v2.(people)
//ascending order
if p1.Age < p2.Age {
return -1
} else if p1.Age > p2.Age {
return 1
}
return 0
}
type intComparator struct{}
func (c *intComparator) Compare(v1 any, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
//ascending order
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func TestBubbleSortForStructSlice(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestBubbleSortForStructSlice")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{}
BubbleSort(peoples, comparator)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
}
func TestBubbleSortForIntSlice(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestBubbleSortForIntSlice")
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
BubbleSort(numbers, comparator)
asssert.Equal([]int{1, 2, 3, 4, 5, 6}, numbers)
}
func TestInsertionSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestInsertionSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{}
InsertionSort(peoples, comparator)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
}
func TestSelectionSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestSelectionSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{}
SelectionSort(peoples, comparator)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
}
func TestShellSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestShellSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{}
ShellSort(peoples, comparator)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
}
func TestQuickSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestQuickSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{}
QuickSort(peoples, comparator)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
}
func TestHeapSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestHeapSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{}
HeapSort(peoples, comparator)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
}
func TestMergeSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestMergeSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{}
MergeSort(peoples, comparator)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
}
func TestCountSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestCountSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{}
sortedPeopleByAge := CountSort(peoples, comparator)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", sortedPeopleByAge)
asssert.Equal(expected, actual)
}

72
compare/compare.go Normal file
View File

@@ -0,0 +1,72 @@
// Copyright 2023 dudaodong@gmail.com. All rights resulterved.
// Use of this source code is governed by MIT license
// Package compare provides a lightweight comparison function on any type.
// reference: https://github.com/stretchr/testify
package compare
import (
"reflect"
"time"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/mathutil"
"golang.org/x/exp/constraints"
)
// operator type
const (
equal = "eq"
lessThan = "lt"
greaterThan = "gt"
lessOrEqual = "le"
greaterOrEqual = "ge"
)
var (
timeType = reflect.TypeOf(time.Time{})
bytesType = reflect.TypeOf([]byte{})
)
// Equal checks if two values are equal or not. (check both type and value)
// Play: https://go.dev/play/p/wmVxR-to4lz
func Equal(left, right any) bool {
return compareValue(equal, left, right)
}
// EqualValue checks if two values are equal or not. (check value only)
// Play: https://go.dev/play/p/fxnna_LLD9u
func EqualValue(left, right any) bool {
ls, rs := convertor.ToString(left), convertor.ToString(right)
return ls == rs
}
// LessThan checks if value `left` less than value `right`.
// Play: https://go.dev/play/p/cYh7FQQj0ne
func LessThan(left, right any) bool {
return compareValue(lessThan, left, right)
}
// GreaterThan checks if value `left` greater than value `right`.
// Play: https://go.dev/play/p/9-NYDFZmIMp
func GreaterThan(left, right any) bool {
return compareValue(greaterThan, left, right)
}
// LessOrEqual checks if value `left` less than or equal to value `right`.
// Play: https://go.dev/play/p/e4T_scwoQzp
func LessOrEqual(left, right any) bool {
return compareValue(lessOrEqual, left, right)
}
// GreaterOrEqual checks if value `left` greater than or equal to value `right`.
// Play: https://go.dev/play/p/vx8mP0U8DFk
func GreaterOrEqual(left, right any) bool {
return compareValue(greaterOrEqual, left, right)
}
// InDelta checks if two values are equal or not within a delta.
// Play: https://go.dev/play/p/TuDdcNtMkjo
func InDelta[T constraints.Integer | constraints.Float](left, right T, delta float64) bool {
return float64(mathutil.Abs(left-right)) <= delta
}

View File

@@ -0,0 +1,196 @@
package compare
import (
"fmt"
"time"
)
func ExampleEqual() {
result1 := Equal(1, 1)
result2 := Equal("1", "1")
result3 := Equal([]int{1, 2, 3}, []int{1, 2, 3})
result4 := Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"})
result5 := Equal(1, "1")
result6 := Equal(1, int64(1))
result7 := Equal([]int{1, 2}, []int{1, 2, 3})
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
fmt.Println(result7)
// Output:
// true
// true
// true
// true
// false
// false
// false
}
func ExampleEqualValue() {
result1 := EqualValue(1, 1)
result2 := EqualValue(int(1), int64(1))
result3 := EqualValue(1, "1")
result4 := EqualValue(1, "2")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// true
// false
}
func ExampleLessThan() {
result1 := LessThan(1, 2)
result2 := LessThan(1.1, 2.2)
result3 := LessThan("a", "b")
time1 := time.Now()
time2 := time1.Add(time.Second)
result4 := LessThan(time1, time2)
result5 := LessThan(2, 1)
result6 := LessThan(1, int64(2))
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
// Output:
// true
// true
// true
// true
// false
// false
}
func ExampleGreaterThan() {
result1 := GreaterThan(2, 1)
result2 := GreaterThan(2.2, 1.1)
result3 := GreaterThan("b", "a")
time1 := time.Now()
time2 := time1.Add(time.Second)
result4 := GreaterThan(time2, time1)
result5 := GreaterThan(1, 2)
result6 := GreaterThan(int64(2), 1)
result7 := GreaterThan("b", "c")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
fmt.Println(result7)
// Output:
// true
// true
// true
// true
// false
// false
// false
}
func ExampleLessOrEqual() {
result1 := LessOrEqual(1, 1)
result2 := LessOrEqual(1.1, 2.2)
result3 := LessOrEqual("a", "b")
time1 := time.Now()
time2 := time1.Add(time.Second)
result4 := LessOrEqual(time1, time2)
result5 := LessOrEqual(2, 1)
result6 := LessOrEqual(1, int64(2))
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
// Output:
// true
// true
// true
// true
// false
// false
}
func ExampleGreaterOrEqual() {
result1 := GreaterOrEqual(1, 1)
result2 := GreaterOrEqual(2.2, 1.1)
result3 := GreaterOrEqual("b", "b")
time1 := time.Now()
time2 := time1.Add(time.Second)
result4 := GreaterOrEqual(time2, time1)
result5 := GreaterOrEqual(1, 2)
result6 := GreaterOrEqual(int64(2), 1)
result7 := GreaterOrEqual("b", "c")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
fmt.Println(result7)
// Output:
// true
// true
// true
// true
// false
// false
// false
}
func ExampleInDelta() {
result1 := InDelta(1, 1, 0)
result2 := InDelta(1, 2, 0)
result3 := InDelta(2.0/3.0, 0.66667, 0.001)
result4 := InDelta(2.0/3.0, 0.0, 0.001)
result5 := InDelta(float64(74.96)-float64(20.48), 54.48, 0)
result6 := InDelta(float64(74.96)-float64(20.48), 54.48, 1e-14)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
// Output:
// true
// false
// true
// false
// false
// true
}

323
compare/compare_internal.go Normal file
View File

@@ -0,0 +1,323 @@
package compare
import (
"bytes"
"encoding/json"
"reflect"
"time"
"github.com/duke-git/lancet/v2/convertor"
)
func compareValue(operator string, left, right any) bool {
leftType, rightType := reflect.TypeOf(left), reflect.TypeOf(right)
if leftType.Kind() != rightType.Kind() {
return false
}
switch leftType.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64, reflect.Bool, reflect.String:
return compareBasicValue(operator, left, right)
case reflect.Struct, reflect.Slice, reflect.Map:
return compareRefValue(operator, left, right, leftType.Kind())
}
return false
}
func compareRefValue(operator string, leftObj, rightObj any, kind reflect.Kind) bool {
leftVal, rightVal := reflect.ValueOf(leftObj), reflect.ValueOf(rightObj)
switch kind {
case reflect.Struct:
// compare time
if leftVal.CanConvert(timeType) {
timeObj1, ok := leftObj.(time.Time)
if !ok {
timeObj1 = leftVal.Convert(timeType).Interface().(time.Time)
}
timeObj2, ok := rightObj.(time.Time)
if !ok {
timeObj2 = rightVal.Convert(timeType).Interface().(time.Time)
}
return compareBasicValue(operator, timeObj1.UnixNano(), timeObj2.UnixNano())
}
// for other struct type, only process equal operator
switch operator {
case equal:
return objectsAreEqualValues(leftObj, rightObj)
}
case reflect.Slice:
// compare []byte
if leftVal.CanConvert(bytesType) {
bytesObj1, ok := leftObj.([]byte)
if !ok {
bytesObj1 = leftVal.Convert(bytesType).Interface().([]byte)
}
bytesObj2, ok := rightObj.([]byte)
if !ok {
bytesObj2 = rightVal.Convert(bytesType).Interface().([]byte)
}
switch operator {
case equal:
if bytes.Compare(bytesObj1, bytesObj2) == 0 {
return true
}
case lessThan:
if bytes.Compare(bytesObj1, bytesObj2) == -1 {
return true
}
case greaterThan:
if bytes.Compare(bytesObj1, bytesObj2) == 1 {
return true
}
case lessOrEqual:
if bytes.Compare(bytesObj1, bytesObj2) <= 0 {
return true
}
case greaterOrEqual:
if bytes.Compare(bytesObj1, bytesObj2) >= 0 {
return true
}
}
}
// for other type slice, only process equal operator
switch operator {
case equal:
return reflect.DeepEqual(leftObj, rightObj)
}
case reflect.Map:
// only process equal operator
switch operator {
case equal:
return reflect.DeepEqual(leftObj, rightObj)
}
}
return false
}
func objectsAreEqualValues(expected, actual interface{}) bool {
if objectsAreEqual(expected, actual) {
return true
}
actualType := reflect.TypeOf(actual)
if actualType == nil {
return false
}
expectedValue := reflect.ValueOf(expected)
if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
// Attempt comparison after type conversion
return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual)
}
return false
}
func objectsAreEqual(expected, actual interface{}) bool {
if expected == nil || actual == nil {
return expected == actual
}
exp, ok := expected.([]byte)
if !ok {
return reflect.DeepEqual(expected, actual)
}
act, ok := actual.([]byte)
if !ok {
return false
}
if exp == nil || act == nil {
return exp == nil && act == nil
}
return bytes.Equal(exp, act)
}
// compareBasic compare basic value: integer, float, string, bool
func compareBasicValue(operator string, leftValue, rightValue any) bool {
if leftValue == nil && rightValue == nil && operator == equal {
return true
}
switch leftVal := leftValue.(type) {
case json.Number:
if left, err := leftVal.Float64(); err == nil {
switch rightVal := rightValue.(type) {
case json.Number:
if right, err := rightVal.Float64(); err == nil {
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
right, err := convertor.ToFloat(rightValue)
if err != nil {
return false
}
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
}
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
left, err := convertor.ToFloat(leftValue)
if err != nil {
return false
}
switch rightVal := rightValue.(type) {
case json.Number:
if right, err := rightVal.Float64(); err == nil {
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
right, err := convertor.ToFloat(rightValue)
if err != nil {
return false
}
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
case string:
left := leftVal
switch right := rightValue.(type) {
case string:
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
case bool:
left := leftVal
switch right := rightValue.(type) {
case bool:
switch operator {
case equal:
if left == right {
return true
}
}
}
}
return false
}

156
compare/compare_test.go Normal file
View File

@@ -0,0 +1,156 @@
package compare
import (
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
func TestEqual(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEqual")
assert.Equal(true, Equal(1, 1))
assert.Equal(true, Equal(int64(1), int64(1)))
assert.Equal(true, Equal("a", "a"))
assert.Equal(true, Equal(true, true))
assert.Equal(true, Equal([]int{1, 2, 3}, []int{1, 2, 3}))
assert.Equal(true, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"}))
assert.Equal(false, Equal(1, 2))
assert.Equal(false, Equal(1, int64(1)))
assert.Equal(false, Equal("a", "b"))
assert.Equal(false, Equal(true, false))
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"}))
time1 := time.Now()
time2 := time1.Add(time.Second)
time3 := time1.Add(time.Second)
assert.Equal(false, Equal(time1, time2))
assert.Equal(true, Equal(time2, time3))
st1 := struct {
A string
B string
}{
A: "a",
B: "b",
}
st2 := struct {
A string
B string
}{
A: "a",
B: "b",
}
st3 := struct {
A string
B string
}{
A: "a1",
B: "b",
}
assert.Equal(true, Equal(st1, st2))
assert.Equal(false, Equal(st1, st3))
}
func TestEqualValue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEqualValue")
assert.Equal(true, EqualValue(1, 1))
assert.Equal(true, EqualValue(int(1), int64(1)))
assert.Equal(true, EqualValue(1, "1"))
assert.Equal(false, EqualValue(1, "2"))
}
func TestLessThan(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLessThan")
assert.Equal(true, LessThan(1, 2))
assert.Equal(true, LessThan(1.1, 2.2))
assert.Equal(true, LessThan("a", "b"))
time1 := time.Now()
time2 := time1.Add(time.Second)
assert.Equal(true, LessThan(time1, time2))
assert.Equal(false, LessThan(1, 1))
assert.Equal(false, LessThan(1, int64(1)))
}
func TestGreaterThan(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGreaterThan")
assert.Equal(true, GreaterThan(2, 1))
assert.Equal(true, GreaterThan(2.2, 1.1))
assert.Equal(true, GreaterThan("b", "a"))
time1 := time.Now()
time2 := time1.Add(time.Second)
assert.Equal(true, GreaterThan(time2, time1))
assert.Equal(false, GreaterThan(1, 2))
assert.Equal(false, GreaterThan(int64(2), 1))
assert.Equal(false, GreaterThan("b", "c"))
}
func TestLessOrEqual(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLessOrEqual")
assert.Equal(true, LessOrEqual(1, 2))
assert.Equal(true, LessOrEqual(1, 1))
assert.Equal(true, LessOrEqual(1.1, 2.2))
assert.Equal(true, LessOrEqual("a", "b"))
time1 := time.Now()
time2 := time1.Add(time.Second)
assert.Equal(true, LessOrEqual(time1, time2))
assert.Equal(false, LessOrEqual(2, 1))
assert.Equal(false, LessOrEqual(1, int64(2)))
}
func TestGreaterOrEqual(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGreaterThan")
assert.Equal(true, GreaterOrEqual(2, 1))
assert.Equal(true, GreaterOrEqual(1, 1))
assert.Equal(true, GreaterOrEqual(2.2, 1.1))
assert.Equal(true, GreaterOrEqual("b", "b"))
time1 := time.Now()
time2 := time1.Add(time.Second)
assert.Equal(true, GreaterOrEqual(time2, time1))
assert.Equal(false, GreaterOrEqual(1, 2))
assert.Equal(false, GreaterOrEqual(int64(2), 1))
assert.Equal(false, GreaterOrEqual("b", "c"))
}
func TestInDelta(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestInDelta")
assert.Equal(true, InDelta(1, 1, 0))
assert.Equal(false, InDelta(1, 2, 0))
assert.Equal(true, InDelta(2.0/3.0, 0.66667, 0.001))
assert.Equal(false, InDelta(2.0/3.0, 0.0, 0.001))
assert.Equal(false, InDelta(float64(74.96)-float64(20.48), 54.48, 0))
assert.Equal(true, InDelta(float64(74.96)-float64(20.48), 54.48, 1e-14))
assert.Equal(false, InDelta(float64(float32(80.45)), float64(80.45), 0))
assert.Equal(true, InDelta(float64(float32(80.45)), float64(80.45), 1e-5))
}

247
concurrency/channel.go Normal file
View File

@@ -0,0 +1,247 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel.
package concurrency
import (
"context"
"sync"
)
// Channel is a logic object which can generate or manipulate go channel
// all methods of Channel are in the book tilted《Concurrency in Go》
type Channel[T any] struct {
}
// NewChannel return a Channel instance
func NewChannel[T any]() *Channel[T] {
return &Channel[T]{}
}
// Generate creates channel, then put values into the channel.
// Play: https://go.dev/play/p/7aB4KyMMp9A
func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T {
dataStream := make(chan T)
go func() {
defer close(dataStream)
for _, v := range values {
select {
case <-ctx.Done():
return
case dataStream <- v:
}
}
}()
return dataStream
}
// Repeat create channel, put values into the channel repeatly until cancel the context.
// Play: https://go.dev/play/p/k5N_ALVmYjE
func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T {
dataStream := make(chan T)
go func() {
defer close(dataStream)
for {
for _, v := range values {
select {
case <-ctx.Done():
return
case dataStream <- v:
}
}
}
}()
return dataStream
}
// RepeatFn create a channel, excutes fn repeatly, and put the result into the channel
// until close context.
// Play: https://go.dev/play/p/4J1zAWttP85
func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T {
dataStream := make(chan T)
go func() {
defer close(dataStream)
for {
select {
case <-ctx.Done():
return
case dataStream <- fn():
}
}
}()
return dataStream
}
// Take create a channel whose values are taken from another channel with limit number.
// Play: https://go.dev/play/p/9Utt-1pDr2J
func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T {
takeStream := make(chan T)
go func() {
defer close(takeStream)
for i := 0; i < number; i++ {
select {
case <-ctx.Done():
return
case takeStream <- <-valueStream:
}
}
}()
return takeStream
}
// FanIn merge multiple channels into one channel.
// Play: https://go.dev/play/p/2VYFMexEvTm
func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T {
out := make(chan T)
go func() {
var wg sync.WaitGroup
wg.Add(len(channels))
for _, c := range channels {
go func(c <-chan T) {
defer wg.Done()
for v := range c {
select {
case <-ctx.Done():
return
case out <- v:
}
}
}(c)
}
wg.Wait()
close(out)
}()
return out
}
// Tee split one chanel into two channels, until cancel the context.
// Play: https://go.dev/play/p/3TQPKnCirrP
func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T) {
out1 := make(chan T)
out2 := make(chan T)
go func() {
defer close(out1)
defer close(out2)
for val := range c.OrDone(ctx, in) {
var out1, out2 = out1, out2
for i := 0; i < 2; i++ {
select {
case <-ctx.Done():
case out1 <- val:
out1 = nil
case out2 <- val:
out2 = nil
}
}
}
}()
return out1, out2
}
// Bridge link multiply channels into one channel.
// Play: https://go.dev/play/p/qmWSy1NVF-Y
func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T {
valStream := make(chan T)
go func() {
defer close(valStream)
for {
var stream <-chan T
select {
case maybeStream, ok := <-chanStream:
if !ok {
return
}
stream = maybeStream
case <-ctx.Done():
return
}
for val := range c.OrDone(ctx, stream) {
select {
case valStream <- val:
case <-ctx.Done():
}
}
}
}()
return valStream
}
// Or read one or more channels into one channel, will close when any readin channel is closed.
// Play: https://go.dev/play/p/Wqz9rwioPww
func (c *Channel[T]) Or(channels ...<-chan T) <-chan T {
switch len(channels) {
case 0:
return nil
case 1:
return channels[0]
}
orDone := make(chan T)
go func() {
defer close(orDone)
switch len(channels) {
case 2:
select {
case <-channels[0]:
case <-channels[1]:
}
default:
select {
case <-channels[0]:
case <-channels[1]:
case <-channels[2]:
case <-c.Or(append(channels[3:], orDone)...):
}
}
}()
return orDone
}
// OrDone read a channel into another channel, will close until cancel context.
// Play: https://go.dev/play/p/lm_GoS6aDjo
func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T {
valStream := make(chan T)
go func() {
defer close(valStream)
for {
select {
case <-ctx.Done():
return
case v, ok := <-channel:
if !ok {
return
}
select {
case valStream <- v:
case <-ctx.Done():
}
}
}
}()
return valStream
}

View File

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

196
concurrency/channel_test.go Normal file
View File

@@ -0,0 +1,196 @@
package concurrency
import (
"context"
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
func TestGenerate(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGenerate")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
intStream := c.Generate(ctx, 1, 2, 3)
assert.Equal(1, <-intStream)
assert.Equal(2, <-intStream)
assert.Equal(3, <-intStream)
}
func TestRepeat(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRepeat")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 5)
assert.Equal(1, <-intStream)
assert.Equal(2, <-intStream)
assert.Equal(1, <-intStream)
assert.Equal(2, <-intStream)
assert.Equal(1, <-intStream)
}
func TestRepeatFn(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRepeatFn")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fn := func() string {
s := "a"
return s
}
c := NewChannel[string]()
dataStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
assert.Equal("a", <-dataStream)
assert.Equal("a", <-dataStream)
assert.Equal("a", <-dataStream)
}
func TestTake(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestTake")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
numbers := make(chan int, 5)
numbers <- 1
numbers <- 2
numbers <- 3
numbers <- 4
numbers <- 5
defer close(numbers)
c := NewChannel[int]()
intStream := c.Take(ctx, numbers, 3)
assert.Equal(1, <-intStream)
assert.Equal(2, <-intStream)
assert.Equal(3, <-intStream)
}
func TestFanIn(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFanIn")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
channels := make([]<-chan int, 3)
for i := 0; i < 3; i++ {
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 3)
}
mergedChannel := c.FanIn(ctx, channels...)
for val := range mergedChannel {
t.Logf("\t%d\n", val)
}
assert.Equal(1, 1)
}
func TestOr(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestOr")
sig := func(after time.Duration) <-chan any {
c := make(chan interface{})
go func() {
defer close(c)
time.Sleep(after)
}()
return c
}
start := time.Now()
c := NewChannel[any]()
<-c.Or(
sig(1*time.Second),
sig(2*time.Second),
sig(3*time.Second),
sig(4*time.Second),
sig(5*time.Second),
)
assert.Equal(true, time.Since(start).Seconds() < 2)
}
func TestOrDone(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestOrDone")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
for val := range c.OrDone(ctx, intStream) {
assert.Equal(1, val)
}
}
func TestTee(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestTee")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
inStream := c.Take(ctx, c.Repeat(ctx, 1), 4)
out1, out2 := c.Tee(ctx, inStream)
for val := range out1 {
val1 := val
val2 := <-out2
assert.Equal(1, val1)
assert.Equal(1, val2)
}
}
func TestBridge(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBridge")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel[int]()
genVals := func() <-chan <-chan int {
chanStream := make(chan (<-chan int))
go func() {
defer close(chanStream)
for i := 0; i < 10; i++ {
stream := make(chan int, 1)
stream <- i
close(stream)
chanStream <- stream
}
}()
return chanStream
}
index := 0
for val := range c.Bridge(ctx, genVals()) {
assert.Equal(index, val)
index++
}
}

85
condition/condition.go Normal file
View File

@@ -0,0 +1,85 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package condition contains some functions for conditional judgment. eg. And, Or, TernaryOperator ...
// The implementation of this package refers to the implementation of carlmjohnson's truthy package, you may find more
// useful information in truthy(https://github.com/carlmjohnson/truthy), thanks carlmjohnson.
package condition
import "reflect"
// Bool returns the truthy value of anything.
// If the value's type has a Bool() bool method, the method is called and returned.
// If the type has an IsZero() bool method, the opposite value is returned.
// Slices and maps are truthy if they have a length greater than zero.
// All other types are truthy if they are not their zero value.
// Play: https://go.dev/play/p/ETzeDJRSvhm
func Bool[T any](value T) bool {
switch m := any(value).(type) {
case interface{ Bool() bool }:
return m.Bool()
case interface{ IsZero() bool }:
return !m.IsZero()
}
return reflectValue(&value)
}
func reflectValue(vp any) bool {
switch rv := reflect.ValueOf(vp).Elem(); rv.Kind() {
case reflect.Map, reflect.Slice:
return rv.Len() != 0
default:
is := rv.IsZero()
return !is
}
}
// And returns true if both a and b are truthy.
// Play: https://go.dev/play/p/W1SSUmt6pvr
func And[T, U any](a T, b U) bool {
return Bool(a) && Bool(b)
}
// Or returns false if neither a nor b is truthy.
// Play: https://go.dev/play/p/UlQTxHaeEkq
func Or[T, U any](a T, b U) bool {
return Bool(a) || Bool(b)
}
// 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
}
// Nor returns true if neither a nor b is truthy.
// Play: https://go.dev/play/p/g2j08F_zZky
func Nor[T, U any](a T, b U) bool {
return !(Bool(a) || Bool(b))
}
// 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)
}
// Nand returns false if both a and b are truthy.
// Play: https://go.dev/play/p/vSRMLxLIbq8
func Nand[T, U any](a T, b U) bool {
return !Bool(a) || !Bool(b)
}
// TernaryOperator checks the value of param `isTrue`, if true return ifValue else return elseValue.
// Play: https://go.dev/play/p/ElllPZY0guT
func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U {
if Bool(isTrue) {
return ifValue
} else {
return elseValue
}
}

View File

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

136
condition/condition_test.go Normal file
View File

@@ -0,0 +1,136 @@
package condition
import (
"errors"
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
type TestStruct struct{}
func TestBool(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBool")
// bool
assert.Equal(false, Bool(false))
assert.Equal(true, Bool(true))
// integer
assert.Equal(false, Bool(0))
assert.Equal(true, Bool(1))
// float
assert.Equal(false, Bool(0.0))
assert.Equal(true, Bool(0.1))
// string
assert.Equal(false, Bool(""))
assert.Equal(true, Bool(" "))
assert.Equal(true, Bool("0"))
// slice
var nums [2]int
assert.Equal(false, Bool(nums))
nums = [2]int{0, 1}
assert.Equal(true, Bool(nums))
// map
assert.Equal(false, Bool(map[string]string{}))
assert.Equal(true, Bool(map[string]string{"a": "a"}))
// channel
var ch chan int
assert.Equal(false, Bool(ch))
ch = make(chan int)
assert.Equal(true, Bool(ch))
// interface
var err error
assert.Equal(false, Bool(err))
err = errors.New("error message")
assert.Equal(true, Bool(err))
// struct
assert.Equal(false, Bool(struct{}{}))
assert.Equal(true, Bool(time.Now()))
// struct pointer
ts := TestStruct{}
assert.Equal(false, Bool(ts))
assert.Equal(true, Bool(&ts))
}
func TestAnd(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAnd")
assert.Equal(false, And(0, 1))
assert.Equal(false, And(0, ""))
assert.Equal(false, And(0, "0"))
assert.Equal(true, And(1, "0"))
}
func TestOr(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestOr")
assert.Equal(false, Or(0, ""))
assert.Equal(true, Or(0, 1))
assert.Equal(true, Or(0, "0"))
assert.Equal(true, Or(1, "0"))
}
func TestXor(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestOr")
assert.Equal(false, Xor(0, 0))
assert.Equal(true, Xor(0, 1))
assert.Equal(true, Xor(1, 0))
assert.Equal(false, Xor(1, 1))
}
func TestNor(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestNor")
assert.Equal(true, Nor(0, 0))
assert.Equal(false, Nor(0, 1))
assert.Equal(false, Nor(1, 0))
assert.Equal(false, Nor(1, 1))
}
func TestXnor(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestXnor")
assert.Equal(true, Xnor(0, 0))
assert.Equal(false, Xnor(0, 1))
assert.Equal(false, Xnor(1, 0))
assert.Equal(true, Xnor(1, 1))
}
func TestNand(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestNand")
assert.Equal(true, Nand(0, 0))
assert.Equal(true, Nand(0, 1))
assert.Equal(true, Nand(1, 0))
assert.Equal(false, Nand(1, 1))
}
func TestTernaryOperator(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TernaryOperator")
trueValue := "1"
falseValue := "0"
assert.Equal(trueValue, TernaryOperator(true, trueValue, falseValue))
}

View File

@@ -6,32 +6,72 @@ package convertor
import (
"bytes"
"encoding/binary"
"encoding/gob"
"encoding/json"
"errors"
"fmt"
"io"
"math"
"reflect"
"regexp"
"strconv"
"strings"
"github.com/duke-git/lancet/v2/structs"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
// ToBool convert string to a boolean
// ToBool convert string to boolean.
// Play: https://go.dev/play/p/ARht2WnGdIN
func ToBool(s string) (bool, error) {
return strconv.ParseBool(s)
}
// ToBytes convert interface to bytes
func ToBytes(data interface{}) ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(data)
if err != nil {
return nil, err
// ToBytes convert value to byte slice.
// Play: https://go.dev/play/p/fAMXYFDvOvr
func ToBytes(value any) ([]byte, error) {
v := reflect.ValueOf(value)
switch value.(type) {
case int, int8, int16, int32, int64:
number := v.Int()
buf := bytes.NewBuffer([]byte{})
buf.Reset()
err := binary.Write(buf, binary.BigEndian, number)
return buf.Bytes(), err
case uint, uint8, uint16, uint32, uint64:
number := v.Uint()
buf := bytes.NewBuffer([]byte{})
buf.Reset()
err := binary.Write(buf, binary.BigEndian, number)
return buf.Bytes(), err
case float32:
number := float32(v.Float())
bits := math.Float32bits(number)
bytes := make([]byte, 4)
binary.BigEndian.PutUint32(bytes, bits)
return bytes, nil
case float64:
number := v.Float()
bits := math.Float64bits(number)
bytes := make([]byte, 8)
binary.BigEndian.PutUint64(bytes, bits)
return bytes, nil
case bool:
return strconv.AppendBool([]byte{}, v.Bool()), nil
case string:
return []byte(v.String()), nil
case []byte:
return v.Bytes(), nil
default:
newValue, err := json.Marshal(value)
return newValue, err
}
return buf.Bytes(), nil
}
// ToChar convert string to char slice
// ToChar convert string to char slice.
// Play: https://go.dev/play/p/JJ1SvbFkVdM
func ToChar(s string) []string {
c := make([]string, 0)
if len(s) == 0 {
@@ -43,133 +83,179 @@ func ToChar(s string) []string {
return c
}
// ToChannel convert a slice of elements to a read-only channel.
// Play: https://go.dev/play/p/hOx_oYZbAnL
func ToChannel[T any](array []T) <-chan T {
ch := make(chan T)
go func() {
for _, item := range array {
ch <- item
}
close(ch)
}()
return ch
}
// ToString convert value to string
func ToString(value interface{}) string {
res := ""
// for number, string, []byte, will convert to string
// for other type (slice, map, array, struct) will call json.Marshal.
// Play: https://go.dev/play/p/nF1zOOslpQq
func ToString(value any) string {
if value == nil {
return res
return ""
}
v := reflect.ValueOf(value)
switch value.(type) {
case float32, float64:
res = strconv.FormatFloat(v.Float(), 'f', -1, 64)
return res
case int, int8, int16, int32, int64:
res = strconv.FormatInt(v.Int(), 10)
return res
case uint, uint8, uint16, uint32, uint64:
res = strconv.FormatUint(v.Uint(), 10)
return res
switch val := value.(type) {
case float32:
return strconv.FormatFloat(float64(val), 'f', -1, 32)
case float64:
return strconv.FormatFloat(val, 'f', -1, 64)
case int:
return strconv.FormatInt(int64(val), 10)
case int8:
return strconv.FormatInt(int64(val), 10)
case int16:
return strconv.FormatInt(int64(val), 10)
case int32:
return strconv.FormatInt(int64(val), 10)
case int64:
return strconv.FormatInt(val, 10)
case uint:
return strconv.FormatUint(uint64(val), 10)
case uint8:
return strconv.FormatUint(uint64(val), 10)
case uint16:
return strconv.FormatUint(uint64(val), 10)
case uint32:
return strconv.FormatUint(uint64(val), 10)
case uint64:
return strconv.FormatUint(val, 10)
case string:
res = v.String()
return res
return val
case []byte:
res = string(v.Bytes())
return res
return string(val)
default:
newValue, _ := json.Marshal(value)
res = string(newValue)
return res
b, err := json.Marshal(val)
if err != nil {
return ""
}
return string(b)
// todo: maybe we should't supprt other type conversion
// v := reflect.ValueOf(value)
// log.Panicf("Unsupported data type: %s ", v.String())
// return ""
}
}
// ToJson convert value to a valid json string
func ToJson(value interface{}) (string, error) {
res, err := json.Marshal(value)
// ToJson convert value to a json string.
// Play: https://go.dev/play/p/2rLIkMmXWvR
func ToJson(value any) (string, error) {
result, err := json.Marshal(value)
if err != nil {
return "", err
}
return string(res), nil
return string(result), nil
}
// ToFloat convert value to a float64, if input is not a float return 0.0 and error
func ToFloat(value interface{}) (float64, error) {
// ToFloat convert value to float64, if input is not a float return 0.0 and error.
// Play: https://go.dev/play/p/4YTmPCibqHJ
func ToFloat(value any) (float64, error) {
v := reflect.ValueOf(value)
res := 0.0
result := 0.0
err := fmt.Errorf("ToInt: unvalid interface type %T", value)
switch value.(type) {
case int, int8, int16, int32, int64:
res = float64(v.Int())
return res, nil
result = float64(v.Int())
return result, nil
case uint, uint8, uint16, uint32, uint64:
res = float64(v.Uint())
return res, nil
result = float64(v.Uint())
return result, nil
case float32, float64:
res = v.Float()
return res, nil
result = v.Float()
return result, nil
case string:
res, err = strconv.ParseFloat(v.String(), 64)
result, err = strconv.ParseFloat(v.String(), 64)
if err != nil {
res = 0.0
result = 0.0
}
return res, err
return result, err
default:
return res, err
return result, err
}
}
// ToInt convert value to a int64, if input is not a numeric format return 0 and error
func ToInt(value interface{}) (int64, error) {
// ToInt convert value to int64 value, if input is not numerical, return 0 and error.
// Play: https://go.dev/play/p/9_h9vIt-QZ_b
func ToInt(value any) (int64, error) {
v := reflect.ValueOf(value)
var res int64
err := fmt.Errorf("ToInt: invalid interface type %T", value)
var result int64
err := fmt.Errorf("ToInt: invalid value type %T", value)
switch value.(type) {
case int, int8, int16, int32, int64:
res = v.Int()
return res, nil
result = v.Int()
return result, nil
case uint, uint8, uint16, uint32, uint64:
res = int64(v.Uint())
return res, nil
result = int64(v.Uint())
return result, nil
case float32, float64:
res = int64(v.Float())
return res, nil
result = int64(v.Float())
return result, nil
case string:
res, err = strconv.ParseInt(v.String(), 0, 64)
result, err = strconv.ParseInt(v.String(), 0, 64)
if err != nil {
res = 0
result = 0
}
return res, err
return result, err
default:
return res, err
return result, err
}
}
// ToPointer returns a pointer to passed value.
// Play: https://go.dev/play/p/ASf_etHNlw1
func ToPointer[T any](value T) *T {
return &value
}
// ToMap convert a slice of structs to a map based on iteratee function.
// Play: https://go.dev/play/p/tVFy7E-t24l
func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V {
result := make(map[K]V, len(array))
for _, item := range array {
k, v := iteratee(item)
result[k] = v
}
return result
}
// StructToMap convert struct to map, only convert exported struct field
// map key is specified same as struct field tag `json` value
func StructToMap(value interface{}) (map[string]interface{}, error) {
v := reflect.ValueOf(value)
t := reflect.TypeOf(value)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", value)
}
res := make(map[string]interface{})
fieldNum := t.NumField()
pattern := `^[A-Z]`
regex := regexp.MustCompile(pattern)
for i := 0; i < fieldNum; i++ {
name := t.Field(i).Name
tag := t.Field(i).Tag.Get("json")
if regex.MatchString(name) && tag != "" {
//res[name] = v.Field(i).Interface()
res[tag] = v.Field(i).Interface()
}
}
return res, nil
// map key is specified same as struct field tag `json` value.
// Play: https://go.dev/play/p/KYGYJqNUBOI
func StructToMap(value any) (map[string]any, error) {
return structs.ToMap(value)
}
// ColorHexToRGB convert hex color to rgb color
// MapToSlice convert map to slice based on iteratee function.
// Play: https://go.dev/play/p/dmX4Ix5V6Wl
func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T {
result := make([]T, 0, len(aMap))
for k, v := range aMap {
result = append(result, iteratee(k, v))
}
return result
}
// ColorHexToRGB convert hex color to rgb color.
// Play: https://go.dev/play/p/o7_ft-JCJBV
func ColorHexToRGB(colorHex string) (red, green, blue int) {
colorHex = strings.TrimPrefix(colorHex, "#")
color64, err := strconv.ParseInt(colorHex, 16, 32)
@@ -180,7 +266,8 @@ func ColorHexToRGB(colorHex string) (red, green, blue int) {
return color >> 16, (color & 0x00FF00) >> 8, color & 0x0000FF
}
// ColorRGBToHex convert rgb color to hex color
// ColorRGBToHex convert rgb color to hex color.
// Play: https://go.dev/play/p/nzKS2Ro87J1
func ColorRGBToHex(red, green, blue int) string {
r := strconv.FormatInt(int64(red), 16)
g := strconv.FormatInt(int64(green), 16)
@@ -198,3 +285,112 @@ func ColorRGBToHex(red, green, blue int) string {
return "#" + r + g + b
}
// EncodeByte encode data to byte slice.
// Play: https://go.dev/play/p/DVmM1G5JfuP
func EncodeByte(data any) ([]byte, error) {
buffer := bytes.NewBuffer(nil)
encoder := gob.NewEncoder(buffer)
err := encoder.Encode(data)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
// DecodeByte decode byte slice data to target object.
// Play: https://go.dev/play/p/zI6xsmuQRbn
func DecodeByte(data []byte, target any) error {
buffer := bytes.NewBuffer(data)
decoder := gob.NewDecoder(buffer)
return decoder.Decode(target)
}
// DeepClone creates a deep copy of passed item.
// can't clone unexported field of struct
// Play: https://go.dev/play/p/j4DP5dquxnk
func DeepClone[T any](src T) T {
c := cloner{
ptrs: map[reflect.Type]map[uintptr]reflect.Value{},
}
result := c.clone(reflect.ValueOf(src))
if result.Kind() == reflect.Invalid {
var zeroValue T
return zeroValue
}
return result.Interface().(T)
}
// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers.
// use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct.
// Play: https://go.dev/play/p/oZujoB5Sgg5
func CopyProperties[T, U any](dst T, src U) error {
dstType, srcType := reflect.TypeOf(dst), reflect.TypeOf(src)
if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
return errors.New("CopyProperties: parameter dst should be struct pointer")
}
if srcType.Kind() == reflect.Ptr {
srcType = srcType.Elem()
}
if srcType.Kind() != reflect.Struct {
return errors.New("CopyProperties: parameter src should be a struct or struct pointer")
}
bytes, err := json.Marshal(src)
if err != nil {
return fmt.Errorf("CopyProperties: unable to marshal src: %s", err)
}
err = json.Unmarshal(bytes, dst)
if err != nil {
return fmt.Errorf("CopyProperties: unable to unmarshal into dst: %s", err)
}
return nil
}
// ToInterface converts reflect value to its interface type.
// Play: https://go.dev/play/p/syqw0-WG7Xd
func ToInterface(v reflect.Value) (value interface{}, ok bool) {
if v.IsValid() && v.CanInterface() {
return v.Interface(), true
}
switch v.Kind() {
case reflect.Bool:
return v.Bool(), true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int(), true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint(), true
case reflect.Float32, reflect.Float64:
return v.Float(), true
case reflect.Complex64, reflect.Complex128:
return v.Complex(), true
case reflect.String:
return v.String(), true
case reflect.Ptr:
return ToInterface(v.Elem())
case reflect.Interface:
return ToInterface(v.Elem())
default:
return nil, false
}
}
// Utf8ToGbk convert utf8 encoding data to GBK encoding data.
// Play: https://go.dev/play/p/9FlIaFLArIL
func Utf8ToGbk(bs []byte) ([]byte, error) {
r := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewEncoder())
b, err := io.ReadAll(r)
return b, err
}
// GbkToUtf8 convert GBK encoding data to utf8 encoding data.
// Play: https://go.dev/play/p/OphmHCN_9u8
func GbkToUtf8(bs []byte) ([]byte, error) {
r := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewDecoder())
b, err := io.ReadAll(r)
return b, err
}

View File

@@ -0,0 +1,393 @@
package convertor
import (
"fmt"
"reflect"
"strconv"
"unicode/utf8"
"github.com/duke-git/lancet/v2/validator"
)
func ExampleToBool() {
cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"}
for i := 0; i < len(cases); i++ {
result, _ := ToBool(cases[i])
fmt.Println(result)
}
// Output:
// true
// true
// true
// false
// false
// false
// false
// false
// false
}
func ExampleToBytes() {
result1, _ := ToBytes(1)
result2, _ := ToBytes("abc")
result3, _ := ToBytes(true)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// [0 0 0 0 0 0 0 1]
// [97 98 99]
// [116 114 117 101]
}
func ExampleToChar() {
result1 := ToChar("")
result2 := ToChar("abc")
result3 := ToChar("1 2#3")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// []
// [a b c]
// [1 2 # 3]
}
func ExampleToChannel() {
ch := ToChannel([]int{1, 2, 3})
result1 := <-ch
result2 := <-ch
result3 := <-ch
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 1
// 2
// 3
}
func ExampleToString() {
result1 := ToString("")
result2 := ToString(nil)
result3 := ToString(0)
result4 := ToString(1.23)
result5 := ToString(true)
result6 := ToString(false)
result7 := ToString([]int{1, 2, 3})
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
fmt.Println(result7)
// Output:
//
//
// 0
// 1.23
// true
// false
// [1,2,3]
}
func ExampleToJson() {
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result1, err := ToJson(aMap)
if err != nil {
fmt.Printf("%v", err)
}
fmt.Println(result1)
// Output:
// {"a":1,"b":2,"c":3}
}
func ExampleToFloat() {
result1, _ := ToFloat("")
result2, _ := ToFloat("abc")
result3, _ := ToFloat("-1")
result4, _ := ToFloat("-.11")
result5, _ := ToFloat("1.23e3")
result6, _ := ToFloat(true)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
// Output:
// 0
// 0
// -1
// -0.11
// 1230
// 0
}
func ExampleToInt() {
result1, _ := ToInt("123")
result2, _ := ToInt("-123")
result3, _ := ToInt(float64(12.3))
result4, _ := ToInt("abc")
result5, _ := ToInt(true)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
// Output:
// 123
// -123
// 12
// 0
// 0
}
func ExampleToPointer() {
result := ToPointer(123)
fmt.Println(*result)
// Output:
// 123
}
func ExampleToMap() {
type Message struct {
name string
code int
}
messages := []Message{
{name: "Hello", code: 100},
{name: "Hi", code: 101},
}
result := ToMap(messages, func(msg Message) (int, string) {
return msg.code, msg.name
})
fmt.Println(result)
// Output:
// map[100:Hello 101:Hi]
}
func ExampleStructToMap() {
type People struct {
Name string `json:"name"`
age int
}
p := People{
"test",
100,
}
pm, _ := StructToMap(p)
fmt.Println(pm)
// Output:
// map[name:test]
}
func ExampleMapToSlice() {
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result := MapToSlice(aMap, func(key string, value int) string {
return key + ":" + strconv.Itoa(value)
})
fmt.Println(result) //[]string{"a:1", "c:3", "b:2"} (random order)
}
func ExampleColorHexToRGB() {
colorHex := "#003366"
r, g, b := ColorHexToRGB(colorHex)
fmt.Println(r, g, b)
// Output:
// 0 51 102
}
func ExampleColorRGBToHex() {
r := 0
g := 51
b := 102
colorHex := ColorRGBToHex(r, g, b)
fmt.Println(colorHex)
// Output:
// #003366
}
func ExampleEncodeByte() {
byteData, _ := EncodeByte("abc")
fmt.Println(byteData)
// Output:
// [6 12 0 3 97 98 99]
}
func ExampleDecodeByte() {
var obj string
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
err := DecodeByte(byteData, &obj)
if err != nil {
return
}
fmt.Println(obj)
// Output:
// abc
}
func ExampleDeepClone() {
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 := 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
}
func ExampleCopyProperties() {
type Disk struct {
Name string `json:"name"`
Total string `json:"total"`
Used string `json:"used"`
Percent float64 `json:"percent"`
}
type DiskVO struct {
Name string `json:"name"`
Total string `json:"total"`
Used string `json:"used"`
Percent float64 `json:"percent"`
}
type Indicator struct {
Id string `json:"id"`
Ip string `json:"ip"`
UpTime string `json:"upTime"`
LoadAvg string `json:"loadAvg"`
Cpu int `json:"cpu"`
Disk []Disk `json:"disk"`
Stop chan bool `json:"-"`
}
type IndicatorVO struct {
Id string `json:"id"`
Ip string `json:"ip"`
UpTime string `json:"upTime"`
LoadAvg string `json:"loadAvg"`
Cpu int64 `json:"cpu"`
Disk []DiskVO `json:"disk"`
}
indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{
{Name: "disk-001", Total: "100", Used: "1", Percent: 10},
{Name: "disk-002", Total: "200", Used: "1", Percent: 20},
{Name: "disk-003", Total: "300", Used: "1", Percent: 30},
}}
indicatorVO := IndicatorVO{}
CopyProperties(&indicatorVO, indicator)
fmt.Println(indicatorVO.Id)
fmt.Println(indicatorVO.Ip)
fmt.Println(len(indicatorVO.Disk))
// Output:
// 001
// 127.0.0.1
// 3
}
func ExampleToInterface() {
val := reflect.ValueOf("abc")
iVal, ok := ToInterface(val)
fmt.Printf("%T\n", iVal)
fmt.Printf("%v\n", iVal)
fmt.Println(ok)
// Output:
// string
// abc
// true
}
func ExampleUtf8ToGbk() {
utf8Data := []byte("hello")
gbkData, _ := Utf8ToGbk(utf8Data)
fmt.Println(utf8.Valid(utf8Data))
fmt.Println(validator.IsGBK(gbkData))
// Output:
// true
// true
}
func ExampleGbkToUtf8() {
gbkData, _ := Utf8ToGbk([]byte("hello"))
utf8Data, _ := GbkToUtf8(gbkData)
fmt.Println(utf8.Valid(utf8Data))
fmt.Println(string(utf8Data))
// Output:
// true
// hello
}

View File

@@ -0,0 +1,216 @@
// Copyright 2023 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package convertor implements some functions to convert data.
package convertor
import "reflect"
type cloner struct {
ptrs map[reflect.Type]map[uintptr]reflect.Value
}
// clone return a duplicate of passed item.
func (c *cloner) clone(v reflect.Value) reflect.Value {
switch v.Kind() {
case reflect.Invalid:
return reflect.ValueOf(nil)
// bool
case reflect.Bool:
return reflect.ValueOf(v.Bool())
//int
case reflect.Int:
return reflect.ValueOf(int(v.Int()))
case reflect.Int8:
return reflect.ValueOf(int8(v.Int()))
case reflect.Int16:
return reflect.ValueOf(int16(v.Int()))
case reflect.Int32:
return reflect.ValueOf(int32(v.Int()))
case reflect.Int64:
return reflect.ValueOf(v.Int())
// uint
case reflect.Uint:
return reflect.ValueOf(uint(v.Uint()))
case reflect.Uint8:
return reflect.ValueOf(uint8(v.Uint()))
case reflect.Uint16:
return reflect.ValueOf(uint16(v.Uint()))
case reflect.Uint32:
return reflect.ValueOf(uint32(v.Uint()))
case reflect.Uint64:
return reflect.ValueOf(v.Uint())
// float
case reflect.Float32:
return reflect.ValueOf(float32(v.Float()))
case reflect.Float64:
return reflect.ValueOf(v.Float())
// complex
case reflect.Complex64:
return reflect.ValueOf(complex64(v.Complex()))
case reflect.Complex128:
return reflect.ValueOf(v.Complex())
// string
case reflect.String:
return reflect.ValueOf(v.String())
// array
case reflect.Array, reflect.Slice:
return c.cloneArray(v)
// map
case reflect.Map:
return c.cloneMap(v)
// Ptr
case reflect.Ptr:
return c.clonePtr(v)
// struct
case reflect.Struct:
return c.cloneStruct(v)
// func
case reflect.Func:
return v
// interface
case reflect.Interface:
return c.clone(v.Elem())
}
return reflect.Zero(v.Type())
}
func (c *cloner) cloneArray(v reflect.Value) reflect.Value {
if v.IsNil() {
return reflect.Zero(v.Type())
}
arr := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
for i := 0; i < v.Len(); i++ {
val := c.clone(v.Index(i))
if !val.IsValid() {
continue
}
item := arr.Index(i)
if !item.CanSet() {
continue
}
item.Set(val.Convert(item.Type()))
}
return arr
}
func (c *cloner) cloneMap(v reflect.Value) reflect.Value {
if v.IsNil() {
return reflect.Zero(v.Type())
}
clonedMap := reflect.MakeMap(v.Type())
for _, key := range v.MapKeys() {
value := v.MapIndex(key)
clonedKey := c.clone(key)
clonedValue := c.clone(value)
if !isNillable(clonedKey) || !clonedKey.IsNil() {
clonedKey = clonedKey.Convert(key.Type())
}
if (!isNillable(clonedValue) || !clonedValue.IsNil()) && clonedValue.IsValid() {
clonedValue = clonedValue.Convert(value.Type())
}
if !clonedValue.IsValid() {
clonedValue = reflect.Zero(clonedMap.Type().Elem())
}
clonedMap.SetMapIndex(clonedKey, clonedValue)
}
return clonedMap
}
func isNillable(v reflect.Value) bool {
switch v.Kind() {
case reflect.Chan, reflect.Interface, reflect.Ptr, reflect.Func:
return true
}
return false
}
func (c *cloner) clonePtr(v reflect.Value) reflect.Value {
if v.IsNil() {
return reflect.Zero(v.Type())
}
var newVal reflect.Value
if v.Elem().CanAddr() {
ptrs, exists := c.ptrs[v.Type()]
if exists {
if newVal, exists := ptrs[v.Elem().UnsafeAddr()]; exists {
return newVal
}
}
}
newVal = c.clone(v.Elem())
if v.Elem().CanAddr() {
ptrs, exists := c.ptrs[v.Type()]
if exists {
if newVal, exists := ptrs[v.Elem().UnsafeAddr()]; exists {
return newVal
}
}
}
clonedPtr := reflect.New(newVal.Type())
clonedPtr.Elem().Set(newVal)
return clonedPtr
}
func (c *cloner) cloneStruct(v reflect.Value) reflect.Value {
clonedStructPtr := reflect.New(v.Type())
clonedStruct := clonedStructPtr.Elem()
if v.CanAddr() {
ptrs := c.ptrs[clonedStructPtr.Type()]
if ptrs == nil {
ptrs = make(map[uintptr]reflect.Value)
c.ptrs[clonedStructPtr.Type()] = ptrs
}
ptrs[v.UnsafeAddr()] = clonedStructPtr
}
for i := 0; i < v.NumField(); i++ {
newStructValue := clonedStruct.Field(i)
if !newStructValue.CanSet() {
continue
}
clonedVal := c.clone(v.Field(i))
if !clonedVal.IsValid() {
continue
}
newStructValue.Set(clonedVal.Convert(newStructValue.Type()))
}
return clonedStruct
}

View File

@@ -2,12 +2,19 @@ package convertor
import (
"fmt"
"reflect"
"strconv"
"testing"
"unicode/utf8"
"github.com/duke-git/lancet/internal"
"github.com/duke-git/lancet/v2/internal"
"github.com/duke-git/lancet/v2/slice"
"github.com/duke-git/lancet/v2/validator"
)
func TestToChar(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToChar")
cases := []string{"", "abc", "1 2#3"}
@@ -21,7 +28,23 @@ func TestToChar(t *testing.T) {
}
}
func TestToChannel(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToChannel")
ch := ToChannel([]int{1, 2, 3})
assert.Equal(1, <-ch)
assert.Equal(2, <-ch)
assert.Equal(3, <-ch)
_, ok := <-ch
assert.Equal(false, ok)
}
func TestToBool(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToBool")
cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"}
@@ -34,28 +57,39 @@ func TestToBool(t *testing.T) {
}
func TestToBytes(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToBytes")
cases := []interface{}{
cases := []any{
0,
false,
"1",
}
expected := [][]byte{
{3, 4, 0, 0},
{3, 2, 0, 0},
{4, 12, 0, 1, 49},
{0, 0, 0, 0, 0, 0, 0, 0},
{102, 97, 108, 115, 101},
{49},
}
for i := 0; i < len(cases); i++ {
actual, _ := ToBytes(cases[i])
assert.Equal(expected[i], actual)
}
bytesData, err := ToBytes("abc")
if err != nil {
t.Error(err)
t.Fail()
}
assert.Equal("abc", ToString(bytesData))
}
func TestToInt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToInt")
cases := []interface{}{"123", "-123", 123,
cases := []any{"123", "-123", 123,
uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
float32(12.3), float64(12.3),
"abc", false, "111111111111111111111111111111111111111"}
@@ -69,9 +103,11 @@ func TestToInt(t *testing.T) {
}
func TestToFloat(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToFloat")
cases := []interface{}{
cases := []any{
"", "-1", "-.11", "1.23e3", ".123e10", "abc",
int(0), int8(1), int16(-1), int32(123), int64(123),
uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
@@ -87,6 +123,8 @@ func TestToFloat(t *testing.T) {
}
func TestToString(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToString")
aMap := make(map[string]int)
@@ -99,7 +137,7 @@ func TestToString(t *testing.T) {
}
aStruct := TestStruct{Name: "TestStruct"}
cases := []interface{}{
cases := []any{
"", nil,
int(0), int8(1), int16(-1), int32(123), int64(123),
uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
@@ -111,9 +149,10 @@ func TestToString(t *testing.T) {
"", "",
"0", "1", "-1",
"123", "123", "123", "123", "123", "123", "123",
"12.3", "12.300000190734863",
"12.3", "12.3",
"true", "false",
"[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello"}
"[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello",
}
for i := 0; i < len(cases); i++ {
actual := ToString(cases[i])
@@ -121,6 +160,8 @@ func TestToString(t *testing.T) {
}
}
func TestToJson(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToJson")
var aMap = map[string]int{"a": 1, "b": 2, "c": 3}
@@ -135,23 +176,83 @@ func TestToJson(t *testing.T) {
assert.Equal("{\"Name\":\"TestStruct\"}", structJsonStr)
}
func TestToMap(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToMap")
type Message struct {
name string
code int
}
messages := []Message{
{name: "Hello", code: 100},
{name: "Hi", code: 101},
}
result := ToMap(messages, func(msg Message) (int, string) {
return msg.code, msg.name
})
expected := map[int]string{100: "Hello", 101: "Hi"}
assert.Equal(expected, result)
}
func TestStructToMap(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestStructToMap")
type People struct {
Name string `json:"name"`
age int
}
p := People{
"test",
100,
}
pm, _ := StructToMap(p)
var expected = map[string]interface{}{"name": "test"}
assert.Equal(expected, pm)
t.Run("StructToMap", func(_ *testing.T) {
type People struct {
Name string `json:"name"`
age int
}
p := People{
"test",
100,
}
pm, _ := StructToMap(p)
var expected = map[string]any{"name": "test"}
assert.Equal(expected, pm)
})
t.Run("StructToMapWithJsonAttr", func(_ *testing.T) {
type People struct {
Name string `json:"name,omitempty"` // json tag with attribute
Phone string `json:"phone"` // json tag without attribute
Sex string `json:"-"` // ignore
age int // no tag
}
p := People{
Phone: "1111",
Sex: "male",
age: 100,
}
pm, _ := StructToMap(p)
var expected = map[string]any{"phone": "1111"}
assert.Equal(expected, pm)
})
}
func TestMapToSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMapToSlice")
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result := MapToSlice(aMap, func(key string, value int) string {
return key + ":" + strconv.Itoa(value)
})
assert.Equal(3, len(result))
assert.Equal(true, slice.Contain(result, "a:1"))
assert.Equal(true, slice.Contain(result, "b:2"))
assert.Equal(true, slice.Contain(result, "c:3"))
}
func TestColorHexToRGB(t *testing.T) {
t.Parallel()
colorHex := "#003366"
r, g, b := ColorHexToRGB(colorHex)
colorRGB := fmt.Sprintf("%d,%d,%d", r, g, b)
@@ -162,6 +263,8 @@ func TestColorHexToRGB(t *testing.T) {
}
func TestColorRGBToHex(t *testing.T) {
t.Parallel()
r := 0
g := 51
b := 102
@@ -171,3 +274,190 @@ func TestColorRGBToHex(t *testing.T) {
assert := internal.NewAssert(t, "TestColorRGBToHex")
assert.Equal(expected, colorHex)
}
func TestToPointer(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToPointer")
result := ToPointer(123)
assert.Equal(*result, 123)
}
func TestEncodeByte(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEncodeByte")
byteData, _ := EncodeByte("abc")
expected := []byte{6, 12, 0, 3, 97, 98, 99}
assert.Equal(expected, byteData)
}
func TestDecodeByte(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDecodeByte")
var obj string
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
err := DecodeByte(byteData, &obj)
assert.IsNil(err)
assert.Equal("abc", obj)
}
func TestDeepClone(t *testing.T) {
t.Parallel()
// assert := internal.NewAssert(t, "TestDeepClone")
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",
},
[]interface{}{1, &Struct{Str: "test"}, Struct{Str: "test2"}},
}
for i, item := range cases {
cloned := DeepClone(item)
if &cloned == &item {
t.Fatalf("[TestDeepClone case #%d failed]: equal pointer", i)
}
if !reflect.DeepEqual(item, cloned) {
t.Fatalf("[TestDeepClone case #%d failed] unequal objects", i)
}
}
}
func TestCopyProperties(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCopyProperties")
type Disk struct {
Name string `json:"name"`
Total string `json:"total"`
Used string `json:"used"`
Percent float64 `json:"percent"`
}
type DiskVO struct {
Name string `json:"name"`
Total string `json:"total"`
Used string `json:"used"`
Percent float64 `json:"percent"`
}
type Indicator struct {
Id string `json:"id"`
Ip string `json:"ip"`
UpTime string `json:"upTime"`
LoadAvg string `json:"loadAvg"`
Cpu int `json:"cpu"`
Disk []Disk `json:"disk"`
Stop chan bool `json:"-"`
}
type IndicatorVO struct {
Id string `json:"id"`
Ip string `json:"ip"`
UpTime string `json:"upTime"`
LoadAvg string `json:"loadAvg"`
Cpu int64 `json:"cpu"`
Disk []DiskVO `json:"disk"`
}
indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{
{Name: "disk-001", Total: "100", Used: "1", Percent: 10},
{Name: "disk-002", Total: "200", Used: "1", Percent: 20},
{Name: "disk-003", Total: "300", Used: "1", Percent: 30},
}}
indicatorVO := IndicatorVO{}
err := CopyProperties(&indicatorVO, indicator)
assert.IsNil(err)
assert.Equal("001", indicatorVO.Id)
assert.Equal("127.0.0.1", indicatorVO.Ip)
assert.Equal(3, len(indicatorVO.Disk))
}
func TestToInterface(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToInterface")
cases := []reflect.Value{
reflect.ValueOf("abc"),
reflect.ValueOf(int(0)), reflect.ValueOf(int8(1)), reflect.ValueOf(int16(-1)), reflect.ValueOf(int32(123)), reflect.ValueOf(int64(123)),
reflect.ValueOf(uint(123)), reflect.ValueOf(uint8(123)), reflect.ValueOf(uint16(123)), reflect.ValueOf(uint32(123)), reflect.ValueOf(uint64(123)),
reflect.ValueOf(float64(12.3)), reflect.ValueOf(float32(12.3)),
reflect.ValueOf(true), reflect.ValueOf(false),
}
expected := []interface{}{
"abc",
0, int8(1), int16(-1), int32(123), int64(123),
uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
float64(12.3), float32(12.3),
true, false,
}
for i := 0; i < len(cases); i++ {
actual, _ := ToInterface(cases[i])
assert.Equal(expected[i], actual)
}
nilVal, ok := ToInterface(reflect.ValueOf(nil))
assert.EqualValues(nil, nilVal)
assert.Equal(false, ok)
}
func TestUtf8ToGbk(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUtf8ToGbk")
utf8Data := []byte("hello")
gbkData, err := Utf8ToGbk(utf8Data)
assert.Equal(true, utf8.Valid(utf8Data))
assert.Equal(true, validator.IsGBK(gbkData))
assert.IsNil(err)
}
func TestGbkToUtf8(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGbkToUtf8")
gbkData, err := Utf8ToGbk([]byte("hello"))
utf8Data, err := GbkToUtf8(gbkData)
assert.IsNil(err)
assert.Equal(true, utf8.Valid(utf8Data))
assert.Equal("hello", string(utf8Data))
}

View File

@@ -1,168 +0,0 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package cryptor implements some util functions to encrypt and decrypt.
// Note:
// 1. for aes crypt function, the `key` param length should be 16, 24 or 32. if not, will panic.
package cryptor
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
)
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
// len(key) should be 16, 24 or 32
func AesEcbEncrypt(data, key []byte) []byte {
cipher, _ := aes.NewCipher(generateAesKey(key))
length := (len(data) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize)
copy(plain, data)
pad := byte(len(plain) - len(data))
for i := len(data); i < len(plain); i++ {
plain[i] = pad
}
encrypted := make([]byte, len(plain))
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
}
return encrypted
}
// AesEcbDecrypt decrypt data with key use AES ECB algorithm
// len(key) should be 16, 24 or 32
func AesEcbDecrypt(encrypted, key []byte) []byte {
cipher, _ := aes.NewCipher(generateAesKey(key))
decrypted := make([]byte, len(encrypted))
//
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
}
trim := 0
if len(decrypted) > 0 {
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
}
return decrypted[:trim]
}
// AesCbcEncrypt encrypt data with key use AES CBC algorithm
// len(key) should be 16, 24 or 32
func AesCbcEncrypt(data, key []byte) []byte {
// len(key) should be 16, 24 or 32
block, _ := aes.NewCipher(key)
blockSize := block.BlockSize()
data = pkcs7Padding(data, blockSize)
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
encrypted := make([]byte, len(data))
blockMode.CryptBlocks(encrypted, data)
return encrypted
}
// AesCbcDecrypt decrypt data with key use AES CBC algorithm
// len(key) should be 16, 24 or 32
func AesCbcDecrypt(encrypted, key []byte) []byte {
block, _ := aes.NewCipher(key)
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
decrypted := make([]byte, len(encrypted))
blockMode.CryptBlocks(decrypted, encrypted)
decrypted = pkcs7UnPadding(decrypted)
return decrypted
}
// AesCtrCrypt encrypt data with key use AES CTR algorithm
// len(key) should be 16, 24 or 32
func AesCtrCrypt(data, key []byte) []byte {
block, _ := aes.NewCipher(key)
iv := bytes.Repeat([]byte("1"), block.BlockSize())
stream := cipher.NewCTR(block, iv)
dst := make([]byte, len(data))
stream.XORKeyStream(dst, data)
return dst
}
// AesCfbEncrypt encrypt data with key use AES CFB algorithm
// len(key) should be 16, 24 or 32
func AesCfbEncrypt(data, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
return encrypted
}
// AesCfbDecrypt decrypt data with key use AES CFB algorithm
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32
func AesCfbDecrypt(encrypted, key []byte) []byte {
block, _ := aes.NewCipher(key)
if len(encrypted) < aes.BlockSize {
panic("encrypted data is too short")
}
iv := encrypted[:aes.BlockSize]
encrypted = encrypted[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(encrypted, encrypted)
return encrypted
}
// AesOfbEncrypt encrypt data with key use AES OFB algorithm
// len(key) should be 16, 24 or 32
func AesOfbEncrypt(data, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
data = pkcs7Padding(data, aes.BlockSize)
encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
return encrypted
}
// AesOfbDecrypt decrypt data with key use AES OFB algorithm
// len(key) should be 16, 24 or 32
func AesOfbDecrypt(data, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
iv := data[:aes.BlockSize]
data = data[aes.BlockSize:]
if len(data)%aes.BlockSize != 0 {
return nil
}
decrypted := make([]byte, len(data))
mode := cipher.NewOFB(block, iv)
mode.XORKeyStream(decrypted, data)
decrypted = pkcs7UnPadding(decrypted)
return decrypted
}

View File

@@ -1,62 +0,0 @@
package cryptor
import (
"testing"
"github.com/duke-git/lancet/internal"
)
func TestAesEcbEncrypt(t *testing.T) {
data := "hello world"
key := "abcdefghijklmnop"
aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key))
aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesEcbEncrypt")
assert.Equal(data, string(aesEcbDecrypt))
}
func TestAesCbcEncrypt(t *testing.T) {
data := "hello world"
key := "abcdefghijklmnop"
aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key))
aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesCbcEncrypt")
assert.Equal(data, string(aesCbcDecrypt))
}
func TestAesCtrCrypt(t *testing.T) {
data := "hello world"
key := "abcdefghijklmnop"
aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key))
aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesCtrCrypt")
assert.Equal(data, string(aesCtrDeCrypt))
}
func TestAesCfbEncrypt(t *testing.T) {
data := "hello world"
key := "abcdefghijklmnop"
aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key))
aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesCfbEncrypt")
assert.Equal(data, string(aesCfbDecrypt))
}
func TestAesOfbEncrypt(t *testing.T) {
data := "hello world"
key := "abcdefghijklmnop"
aesOfbEncrypt := AesOfbEncrypt([]byte(data), []byte(key))
aesOfbDecrypt := AesOfbDecrypt(aesOfbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesOfbEncrypt")
assert.Equal(data, string(aesOfbDecrypt))
}

View File

@@ -19,25 +19,52 @@ import (
"os"
)
// Base64StdEncode encode string with base64 encoding
// Base64StdEncode encode string with base64 encoding.
// Play: https://go.dev/play/p/VOaUyQUreoK
func Base64StdEncode(s string) string {
return base64.StdEncoding.EncodeToString([]byte(s))
}
// Base64StdDecode decode a base64 encoded string
// Base64StdDecode decode a base64 encoded string.
// Play: https://go.dev/play/p/RWQylnJVgIe
func Base64StdDecode(s string) string {
b, _ := base64.StdEncoding.DecodeString(s)
return string(b)
}
// Md5String return the md5 value of string
// Md5String return the md5 value of string.
// Play: https://go.dev/play/p/1bLcVetbTOI
func Md5String(s string) string {
h := md5.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
// Md5File return the md5 value of file
// Md5StringWithBase64 return the md5 value of string with base64.
// Play: https://go.dev/play/p/Lx4gH7Vdr5_y
func Md5StringWithBase64(s string) string {
h := md5.New()
h.Write([]byte(s))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
// Md5Byte return the md5 string of byte slice.
// Play: https://go.dev/play/p/suraalH8lyC
func Md5Byte(data []byte) string {
h := md5.New()
h.Write(data)
return hex.EncodeToString(h.Sum(nil))
}
// Md5ByteWithBase64 return the md5 string of byte slice with base64.
// Play: https://go.dev/play/p/Tcb-Z7LN2ax
func Md5ByteWithBase64(data []byte) string {
h := md5.New()
h.Write(data)
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
// Md5File return the md5 value of file.
func Md5File(filename string) (string, error) {
if fileInfo, err := os.Stat(filename); err != nil {
return "", err
@@ -69,51 +96,114 @@ func Md5File(filename string) (string, error) {
return checksum, nil
}
// HmacMd5 return the hmac hash of string use md5
func HmacMd5(data, key string) string {
// HmacMd5 return the hmac hash of string use md5.
// Play: https://go.dev/play/p/uef0q1fz53I
func HmacMd5(str, key string) string {
h := hmac.New(md5.New, []byte(key))
h.Write([]byte(str))
return hex.EncodeToString(h.Sum([]byte("")))
}
// HmacMd5WithBase64 return the hmac hash of string use md5 with base64.
// https://go.dev/play/p/UY0ng2AefFC
func HmacMd5WithBase64(data, key string) string {
h := hmac.New(md5.New, []byte(key))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum([]byte("")))
return base64.StdEncoding.EncodeToString(h.Sum([]byte("")))
}
// HmacSha1 return the hmac hash of string use sha1
func HmacSha1(data, key string) string {
// HmacSha1 return the hmac hash of string use sha1.
// Play: https://go.dev/play/p/1UI4oQ4WXKM
func HmacSha1(str, key string) string {
h := hmac.New(sha1.New, []byte(key))
h.Write([]byte(data))
h.Write([]byte(str))
return hex.EncodeToString(h.Sum([]byte("")))
}
// HmacSha256 return the hmac hash of string use sha256
func HmacSha256(data, key string) string {
// HmacSha1WithBase64 return the hmac hash of string use sha1 with base64.
// Play: https://go.dev/play/p/47JmmGrnF7B
func HmacSha1WithBase64(str, key string) string {
h := hmac.New(sha1.New, []byte(key))
h.Write([]byte(str))
return base64.StdEncoding.EncodeToString(h.Sum([]byte("")))
}
// HmacSha256 return the hmac hash of string use sha256.
// Play: https://go.dev/play/p/HhpwXxFhhC0
func HmacSha256(str, key string) string {
h := hmac.New(sha256.New, []byte(key))
h.Write([]byte(data))
h.Write([]byte(str))
return hex.EncodeToString(h.Sum([]byte("")))
}
// HmacSha512 return the hmac hash of string use sha512
func HmacSha512(data, key string) string {
// HmacSha256WithBase64 return the hmac hash of string use sha256 with base64.
// Play: https://go.dev/play/p/EKbkUvPTLwO
func HmacSha256WithBase64(str, key string) string {
h := hmac.New(sha256.New, []byte(key))
h.Write([]byte(str))
return base64.StdEncoding.EncodeToString(h.Sum([]byte("")))
}
// HmacSha512 return the hmac hash of string use sha512.
// Play: https://go.dev/play/p/59Od6m4A0Ud
func HmacSha512(str, key string) string {
h := hmac.New(sha512.New, []byte(key))
h.Write([]byte(data))
h.Write([]byte(str))
return hex.EncodeToString(h.Sum([]byte("")))
}
// Sha1 return the sha1 value (SHA-1 hash algorithm) of string
func Sha1(data string) string {
// HmacSha512WithBase64 return the hmac hash of string use sha512 with base64.
// Play: https://go.dev/play/p/c6dSe3E2ydU
func HmacSha512WithBase64(str, key string) string {
h := hmac.New(sha512.New, []byte(key))
h.Write([]byte(str))
return base64.StdEncoding.EncodeToString(h.Sum([]byte("")))
}
// Sha1 return the sha1 value (SHA-1 hash algorithm) of string.
// Play: https://go.dev/play/p/_m_uoD1deMT
func Sha1(str string) string {
sha1 := sha1.New()
sha1.Write([]byte(data))
sha1.Write([]byte(str))
return hex.EncodeToString(sha1.Sum([]byte("")))
}
// Sha256 return the sha256 value (SHA256 hash algorithm) of string
func Sha256(data string) string {
// Sha1WithBase64 return the sha1 value (SHA-1 hash algorithm) of base64 string.
// Play: https://go.dev/play/p/fSyx-Gl2l2-
func Sha1WithBase64(str string) string {
sha1 := sha1.New()
sha1.Write([]byte(str))
return base64.StdEncoding.EncodeToString(sha1.Sum([]byte("")))
}
// Sha256 return the sha256 value (SHA256 hash algorithm) of string.
// Play: https://go.dev/play/p/tU9tfBMIAr1
func Sha256(str string) string {
sha256 := sha256.New()
sha256.Write([]byte(data))
sha256.Write([]byte(str))
return hex.EncodeToString(sha256.Sum([]byte("")))
}
// Sha512 return the sha512 value (SHA512 hash algorithm) of string
func Sha512(data string) string {
// Sha256WithBase64 return the sha256 value (SHA256 hash algorithm) of base64 string.
// Play: https://go.dev/play/p/85IXJHIal1k
func Sha256WithBase64(str string) string {
sha256 := sha256.New()
sha256.Write([]byte(str))
return base64.StdEncoding.EncodeToString(sha256.Sum([]byte("")))
}
// Sha512 return the sha512 value (SHA512 hash algorithm) of string.
// Play: https://go.dev/play/p/3WsvLYZxsHa
func Sha512(str string) string {
sha512 := sha512.New()
sha512.Write([]byte(data))
sha512.Write([]byte(str))
return hex.EncodeToString(sha512.Sum([]byte("")))
}
// Sha512WithBase64 return the sha512 value (SHA512 hash algorithm) of base64 string.
// Play: https://go.dev/play/p/q_fY2rA-k5I
func Sha512WithBase64(str string) string {
sha512 := sha512.New()
sha512.Write([]byte(str))
return base64.StdEncoding.EncodeToString(sha512.Sum([]byte("")))
}

View File

@@ -3,25 +3,55 @@ package cryptor
import (
"testing"
"github.com/duke-git/lancet/internal"
"github.com/duke-git/lancet/v2/internal"
)
func TestBase64StdEncode(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBase64StdEncode")
assert.Equal("aGVsbG8gd29ybGQ=", Base64StdEncode("hello world"))
}
func TestBase64StdDecode(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBase64StdDecode")
assert.Equal("hello world", Base64StdDecode("aGVsbG8gd29ybGQ="))
}
func TestMd5String(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMd5String")
assert.Equal("5d41402abc4b2a76b9719d911017c592", Md5String("hello"))
}
func TestMd5StringWithBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMd5StringWithBase64")
assert.Equal("XUFAKrxLKna5cZ2REBfFkg==", Md5StringWithBase64("hello"))
}
func TestMd5Byte(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMd5Byte")
data := []byte{'a'}
assert.Equal("0cc175b9c0f1b6a831c399e269772661", Md5Byte(data))
}
func TestMd5ByteWithBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMd5ByteWithBase64")
assert.Equal("XUFAKrxLKna5cZ2REBfFkg==", Md5ByteWithBase64([]byte("hello")))
}
func TestMd5File(t *testing.T) {
t.Parallel()
fileMd5, err := Md5File("./basic.go")
assert := internal.NewAssert(t, "TestMd5File")
assert.IsNotNil(fileMd5)
@@ -29,11 +59,22 @@ func TestMd5File(t *testing.T) {
}
func TestHmacMd5(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHmacMd5")
assert.Equal("5f4c9faaff0a1ad3007d9ddc06abe36d", HmacMd5("hello world", "12345"))
}
func TestHmacMd5WithBase64(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHmacMd5WithBase64")
assert.Equal("6DQwbquJLYclJdSRinpjmg==", HmacMd5WithBase64("hello", "12345"))
}
func TestHmacSha1(t *testing.T) {
t.Parallel()
s := "hello world"
key := "12345"
hmacSha1 := HmacSha1(s, key)
@@ -43,17 +84,45 @@ func TestHmacSha1(t *testing.T) {
assert.Equal(expected, hmacSha1)
}
func TestHmacSha256(t *testing.T) {
s := "hello world"
func TestHmacSha1WithBase64(t *testing.T) {
t.Parallel()
s := "hello"
key := "12345"
hmacSha256 := HmacSha256(s, key)
hmacSha1 := HmacSha1WithBase64(s, key)
expected := "XGqdsMzLkuNu0DI/0Jt/k23prOA="
assert := internal.NewAssert(t, "TestHmacSha1")
assert.Equal(expected, hmacSha1)
}
func TestHmacSha256(t *testing.T) {
t.Parallel()
str := "hello world"
key := "12345"
hmacSha256 := HmacSha256(str, key)
expected := "9dce2609f2d67d41f74c7f9efc8ccd44370d41ad2de52982627588dfe7289ab8"
assert := internal.NewAssert(t, "TestHmacSha256")
assert.Equal(expected, hmacSha256)
}
func TestHmacSha256WithBase64(t *testing.T) {
t.Parallel()
str := "hello"
key := "12345"
hms := HmacSha256WithBase64(str, key)
expected := "MVu5PE6YmGK6Ccti4F1zpfN2yzbw14btqwwyDQWf3nU="
assert := internal.NewAssert(t, "TestHmacSha256WithBase64")
assert.Equal(expected, hms)
}
func TestHmacSha512(t *testing.T) {
t.Parallel()
s := "hello world"
key := "12345"
hmacSha512 := HmacSha512(s, key)
@@ -63,7 +132,21 @@ func TestHmacSha512(t *testing.T) {
assert.Equal(expected, hmacSha512)
}
func TestHmacSha512WithBase64(t *testing.T) {
t.Parallel()
str := "hello"
key := "12345"
hms := HmacSha512WithBase64(str, key)
expected := "3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A=="
assert := internal.NewAssert(t, "TestHmacSha512WithBase64")
assert.Equal(expected, hms)
}
func TestSha1(t *testing.T) {
t.Parallel()
s := "hello world"
sha1 := Sha1(s)
expected := "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"
@@ -72,7 +155,19 @@ func TestSha1(t *testing.T) {
assert.Equal(expected, sha1)
}
func TestSha1WithBase64(t *testing.T) {
t.Parallel()
str := Sha1WithBase64("hello")
expected := "qvTGHdzF6KLavt4PO0gs2a6pQ00="
assert := internal.NewAssert(t, "TestSha1WithBase64")
assert.Equal(expected, str)
}
func TestSha256(t *testing.T) {
t.Parallel()
s := "hello world"
sha256 := Sha256(s)
expected := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
@@ -81,7 +176,19 @@ func TestSha256(t *testing.T) {
assert.Equal(expected, sha256)
}
func TestSha256WithBase64(t *testing.T) {
t.Parallel()
str := Sha256WithBase64("hello")
expected := "LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="
assert := internal.NewAssert(t, "TestSha256WithBase64")
assert.Equal(expected, str)
}
func TestSha512(t *testing.T) {
t.Parallel()
s := "hello world"
sha512 := Sha512(s)
expected := "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f"
@@ -89,3 +196,13 @@ func TestSha512(t *testing.T) {
assert := internal.NewAssert(t, "TestSha512")
assert.Equal(expected, sha512)
}
func TestSha512WithBase64(t *testing.T) {
t.Parallel()
str := Sha512WithBase64("hello")
expected := "m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw=="
assert := internal.NewAssert(t, "TestSha512WithBase64")
assert.Equal(expected, str)
}

507
cryptor/crypto.go Normal file
View File

@@ -0,0 +1,507 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package cryptor implements some util functions to encrypt and decrypt.
// Note:
// 1. for aes crypt function, the `key` param length should be 16, 24 or 32. if not, will panic.
package cryptor
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"io"
"os"
)
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/jT5irszHx-j
func AesEcbEncrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
}
length := (len(data) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize)
copy(plain, data)
pad := byte(len(plain) - len(data))
for i := len(data); i < len(plain); i++ {
plain[i] = pad
}
encrypted := make([]byte, len(plain))
cipher, _ := aes.NewCipher(generateAesKey(key, size))
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
}
return encrypted
}
// AesEcbDecrypt decrypt data with key use AES ECB algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/jT5irszHx-j
func AesEcbDecrypt(encrypted, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
}
cipher, _ := aes.NewCipher(generateAesKey(key, size))
decrypted := make([]byte, len(encrypted))
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
}
trim := 0
if len(decrypted) > 0 {
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
}
return decrypted[:trim]
}
// AesCbcEncrypt encrypt data with key use AES CBC algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/IOq_g8_lKZD
func AesCbcEncrypt(data, key []byte) []byte {
block, _ := aes.NewCipher(key)
data = pkcs7Padding(data, block.BlockSize())
encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(encrypted[aes.BlockSize:], data)
return encrypted
}
// AesCbcDecrypt decrypt data with key use AES CBC algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/IOq_g8_lKZD
func AesCbcDecrypt(encrypted, key []byte) []byte {
block, _ := aes.NewCipher(key)
iv := encrypted[:aes.BlockSize]
encrypted = encrypted[aes.BlockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(encrypted, encrypted)
decrypted := pkcs7UnPadding(encrypted)
return decrypted
}
// AesCtrCrypt encrypt data with key use AES CTR algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/SpaZO0-5Nsp
func AesCtrCrypt(data, key []byte) []byte {
block, _ := aes.NewCipher(key)
iv := bytes.Repeat([]byte("1"), block.BlockSize())
stream := cipher.NewCTR(block, iv)
dst := make([]byte, len(data))
stream.XORKeyStream(dst, data)
return dst
}
// AesCfbEncrypt encrypt data with key use AES CFB algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/tfkF10B13kH
func AesCfbEncrypt(data, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
return encrypted
}
// AesCfbDecrypt decrypt data with key use AES CFB algorithm
// 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 {
if len(encrypted) < aes.BlockSize {
panic("encrypted data is too short")
}
block, _ := aes.NewCipher(key)
iv := encrypted[:aes.BlockSize]
encrypted = encrypted[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(encrypted, encrypted)
return encrypted
}
// AesOfbEncrypt encrypt data with key use AES OFB algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/VtHxtkUj-3F
func AesOfbEncrypt(data, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
data = pkcs7Padding(data, aes.BlockSize)
encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
return encrypted
}
// AesOfbDecrypt decrypt data with key use AES OFB algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/VtHxtkUj-3F
func AesOfbDecrypt(data, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
iv := data[:aes.BlockSize]
data = data[aes.BlockSize:]
if len(data)%aes.BlockSize != 0 {
return nil
}
decrypted := make([]byte, len(data))
mode := cipher.NewOFB(block, iv)
mode.XORKeyStream(decrypted, data)
decrypted = pkcs7UnPadding(decrypted)
return decrypted
}
// DesEcbEncrypt encrypt data with key use DES ECB algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/8qivmPeZy4P
func DesEcbEncrypt(data, key []byte) []byte {
length := (len(data) + des.BlockSize) / des.BlockSize
plain := make([]byte, length*des.BlockSize)
copy(plain, data)
pad := byte(len(plain) - len(data))
for i := len(data); i < len(plain); i++ {
plain[i] = pad
}
encrypted := make([]byte, len(plain))
cipher, _ := des.NewCipher(generateDesKey(key))
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
}
return encrypted
}
// DesEcbDecrypt decrypt data with key use DES ECB algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/8qivmPeZy4P
func DesEcbDecrypt(encrypted, key []byte) []byte {
cipher, _ := des.NewCipher(generateDesKey(key))
decrypted := make([]byte, len(encrypted))
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
}
trim := 0
if len(decrypted) > 0 {
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
}
return decrypted[:trim]
}
// DesCbcEncrypt encrypt data with key use DES CBC algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/4cC4QvWfe3_1
func DesCbcEncrypt(data, key []byte) []byte {
block, _ := des.NewCipher(key)
data = pkcs7Padding(data, block.BlockSize())
encrypted := make([]byte, des.BlockSize+len(data))
iv := encrypted[:des.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(encrypted[des.BlockSize:], data)
return encrypted
}
// DesCbcDecrypt decrypt data with key use DES CBC algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/4cC4QvWfe3_1
func DesCbcDecrypt(encrypted, key []byte) []byte {
block, _ := des.NewCipher(key)
iv := encrypted[:des.BlockSize]
encrypted = encrypted[des.BlockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(encrypted, encrypted)
decrypted := pkcs7UnPadding(encrypted)
return decrypted
}
// DesCtrCrypt encrypt data with key use DES CTR algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/9-T6OjKpcdw
func DesCtrCrypt(data, key []byte) []byte {
block, _ := des.NewCipher(key)
iv := bytes.Repeat([]byte("1"), block.BlockSize())
stream := cipher.NewCTR(block, iv)
dst := make([]byte, len(data))
stream.XORKeyStream(dst, data)
return dst
}
// DesCfbEncrypt encrypt data with key use DES CFB algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/y-eNxcFBlxL
func DesCfbEncrypt(data, key []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}
encrypted := make([]byte, des.BlockSize+len(data))
iv := encrypted[:des.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(encrypted[des.BlockSize:], data)
return encrypted
}
// DesCfbDecrypt decrypt data with key use DES CFB algorithm
// 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 {
block, _ := des.NewCipher(key)
if len(encrypted) < des.BlockSize {
panic("encrypted data is too short")
}
iv := encrypted[:des.BlockSize]
encrypted = encrypted[des.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(encrypted, encrypted)
return encrypted
}
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/74KmNadjN1J
func DesOfbEncrypt(data, key []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}
data = pkcs7Padding(data, des.BlockSize)
encrypted := make([]byte, des.BlockSize+len(data))
iv := encrypted[:des.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(encrypted[des.BlockSize:], data)
return encrypted
}
// DesOfbDecrypt decrypt data with key use DES OFB algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/74KmNadjN1J
func DesOfbDecrypt(data, key []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}
iv := data[:des.BlockSize]
data = data[des.BlockSize:]
if len(data)%des.BlockSize != 0 {
return nil
}
decrypted := make([]byte, len(data))
mode := cipher.NewOFB(block, iv)
mode.XORKeyStream(decrypted, data)
decrypted = pkcs7UnPadding(decrypted)
return decrypted
}
// GenerateRsaKey create rsa private and public pemo file.
// Play: https://go.dev/play/p/zutRHrDqs0X
func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
// private key
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
return err
}
derText := x509.MarshalPKCS1PrivateKey(privateKey)
block := pem.Block{
Type: "rsa private key",
Bytes: derText,
}
file, err := os.Create(priKeyFile)
if err != nil {
panic(err)
}
err = pem.Encode(file, &block)
if err != nil {
return err
}
file.Close()
// public key
publicKey := privateKey.PublicKey
derpText, err := x509.MarshalPKIXPublicKey(&publicKey)
if err != nil {
return err
}
block = pem.Block{
Type: "rsa public key",
Bytes: derpText,
}
file, err = os.Create(pubKeyFile)
if err != nil {
return err
}
err = pem.Encode(file, &block)
if err != nil {
return err
}
file.Close()
return nil
}
// RsaEncrypt encrypt data with ras algorithm.
// Play: https://go.dev/play/p/rDqTT01SPkZ
func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
file, err := os.Open(pubKeyFileName)
if err != nil {
panic(err)
}
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
defer file.Close()
buf := make([]byte, fileInfo.Size())
_, err = file.Read(buf)
if err != nil {
panic(err)
}
block, _ := pem.Decode(buf)
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
panic(err)
}
pubKey := pubInterface.(*rsa.PublicKey)
cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, data)
if err != nil {
panic(err)
}
return cipherText
}
// RsaDecrypt decrypt data with ras algorithm.
// Play: https://go.dev/play/p/rDqTT01SPkZ
func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
file, err := os.Open(privateKeyFileName)
if err != nil {
panic(err)
}
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, fileInfo.Size())
defer file.Close()
_, err = file.Read(buf)
if err != nil {
panic(err)
}
block, _ := pem.Decode(buf)
priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
panic(err)
}
plainText, err := rsa.DecryptPKCS1v15(rand.Reader, priKey, data)
if err != nil {
panic(err)
}
return plainText
}

View File

@@ -0,0 +1,486 @@
package cryptor
import "fmt"
func ExampleAesEcbEncrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesEcbEncrypt([]byte(data), []byte(key))
decrypted := AesEcbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleAesEcbDecrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesEcbEncrypt([]byte(data), []byte(key))
decrypted := AesEcbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleAesCbcEncrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesCbcEncrypt([]byte(data), []byte(key))
decrypted := AesCbcDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleAesCbcDecrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesCbcEncrypt([]byte(data), []byte(key))
decrypted := AesCbcDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleAesCtrCrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesCtrCrypt([]byte(data), []byte(key))
decrypted := AesCtrCrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleAesCfbEncrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesCfbEncrypt([]byte(data), []byte(key))
decrypted := AesCfbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleAesCfbDecrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesCfbEncrypt([]byte(data), []byte(key))
decrypted := AesCfbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleAesOfbEncrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesOfbEncrypt([]byte(data), []byte(key))
decrypted := AesOfbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleAesOfbDecrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesOfbEncrypt([]byte(data), []byte(key))
decrypted := AesOfbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesEcbEncrypt() {
data := "hello"
key := "abcdefgh"
encrypted := DesEcbEncrypt([]byte(data), []byte(key))
decrypted := DesEcbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesEcbDecrypt() {
data := "hello"
key := "abcdefgh"
encrypted := DesEcbEncrypt([]byte(data), []byte(key))
decrypted := DesEcbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesCbcEncrypt() {
data := "hello"
key := "abcdefgh"
encrypted := DesCbcEncrypt([]byte(data), []byte(key))
decrypted := DesCbcDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesCbcDecrypt() {
data := "hello"
key := "abcdefgh"
encrypted := DesCbcEncrypt([]byte(data), []byte(key))
decrypted := DesCbcDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesCtrCrypt() {
data := "hello"
key := "abcdefgh"
encrypted := DesCtrCrypt([]byte(data), []byte(key))
decrypted := DesCtrCrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesCfbEncrypt() {
data := "hello"
key := "abcdefgh"
encrypted := DesCfbEncrypt([]byte(data), []byte(key))
decrypted := DesCfbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesCfbDecrypt() {
data := "hello"
key := "abcdefgh"
encrypted := DesCfbEncrypt([]byte(data), []byte(key))
decrypted := DesCfbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesOfbEncrypt() {
data := "hello"
key := "abcdefgh"
encrypted := DesOfbEncrypt([]byte(data), []byte(key))
decrypted := DesOfbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesOfbDecrypt() {
data := "hello"
key := "abcdefgh"
encrypted := DesOfbEncrypt([]byte(data), []byte(key))
decrypted := DesOfbDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleGenerateRsaKey() {
// Create ras private and public pem file
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
if err != nil {
return
}
fmt.Println("foo")
// Output:
// foo
}
func ExampleRsaEncrypt() {
// Create ras private and public pem file
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
if err != nil {
return
}
data := []byte("hello")
encrypted := RsaEncrypt(data, "rsa_public.pem")
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleRsaDecrypt() {
// Create ras private and public pem file
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
if err != nil {
return
}
data := []byte("hello")
encrypted := RsaEncrypt(data, "rsa_public.pem")
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleBase64StdEncode() {
base64Str := Base64StdEncode("hello")
fmt.Println(base64Str)
// Output:
// aGVsbG8=
}
func ExampleBase64StdDecode() {
str := Base64StdDecode("aGVsbG8=")
fmt.Println(str)
// Output:
// hello
}
func ExampleHmacMd5() {
str := "hello"
key := "12345"
hms := HmacMd5(str, key)
fmt.Println(hms)
// Output:
// e834306eab892d872525d4918a7a639a
}
func ExampleHmacMd5WithBase64() {
str := "hello"
key := "12345"
hms := HmacMd5WithBase64(str, key)
fmt.Println(hms)
// Output:
// 6DQwbquJLYclJdSRinpjmg==
}
func ExampleHmacSha1() {
str := "hello"
key := "12345"
hms := HmacSha1(str, key)
fmt.Println(hms)
// Output:
// 5c6a9db0cccb92e36ed0323fd09b7f936de9ace0
}
func ExampleHmacSha1WithBase64() {
str := "hello"
key := "12345"
hms := HmacSha1WithBase64(str, key)
fmt.Println(hms)
// Output:
// XGqdsMzLkuNu0DI/0Jt/k23prOA=
}
func ExampleHmacSha256() {
str := "hello"
key := "12345"
hms := HmacSha256(str, key)
fmt.Println(hms)
// Output:
// 315bb93c4e989862ba09cb62e05d73a5f376cb36f0d786edab0c320d059fde75
}
func ExampleHmacSha256WithBase64() {
str := "hello"
key := "12345"
hms := HmacSha256WithBase64(str, key)
fmt.Println(hms)
// Output:
// MVu5PE6YmGK6Ccti4F1zpfN2yzbw14btqwwyDQWf3nU=
}
func ExampleHmacSha512() {
str := "hello"
key := "12345"
hms := HmacSha512(str, key)
fmt.Println(hms)
// Output:
// dd8f1290a9dd23d354e2526d9a2e9ce8cffffdd37cb320800d1c6c13d2efc363288376a196c5458daf53f8e1aa6b45a6d856303d5c0a2064bff9785861d48cfc
}
func ExampleHmacSha512WithBase64() {
str := "hello"
key := "12345"
hms := HmacSha512WithBase64(str, key)
fmt.Println(hms)
// Output:
// 3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A==
}
func ExampleMd5String() {
md5Str := Md5String("hello")
fmt.Println(md5Str)
// Output:
// 5d41402abc4b2a76b9719d911017c592
}
func ExampleMd5StringWithBase64() {
md5Str := Md5StringWithBase64("hello")
fmt.Println(md5Str)
// Output:
// XUFAKrxLKna5cZ2REBfFkg==
}
func ExampleMd5Byte() {
md5Str := Md5Byte([]byte{'a'})
fmt.Println(md5Str)
// Output:
// 0cc175b9c0f1b6a831c399e269772661
}
func ExampleMd5ByteWithBase64() {
md5Str := Md5ByteWithBase64([]byte("hello"))
fmt.Println(md5Str)
// Output:
// XUFAKrxLKna5cZ2REBfFkg==
}
func ExampleSha1() {
result := Sha1("hello")
fmt.Println(result)
// Output:
// aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
}
func ExampleSha1WithBase64() {
result := Sha1WithBase64("hello")
fmt.Println(result)
// Output:
// qvTGHdzF6KLavt4PO0gs2a6pQ00=
}
func ExampleSha256() {
result := Sha256("hello")
fmt.Println(result)
// Output:
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
}
func ExampleSha256WithBase64() {
result := Sha256WithBase64("hello")
fmt.Println(result)
// Output:
// LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=
}
func ExampleSha512() {
result := Sha512("hello")
fmt.Println(result)
// Output:
// 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043
}
func ExampleSha512WithBase64() {
result := Sha512WithBase64("hello")
fmt.Println(result)
// Output:
// m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==
}

View File

@@ -2,11 +2,11 @@ package cryptor
import "bytes"
func generateAesKey(key []byte) []byte {
genKey := make([]byte, 16)
func generateAesKey(key []byte, size int) []byte {
genKey := make([]byte, size)
copy(genKey, key)
for i := 16; i < len(key); {
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
for i := size; i < len(key); {
for j := 0; j < size && i < len(key); j, i = j+1, i+1 {
genKey[j] ^= key[i]
}
}

152
cryptor/crypto_test.go Normal file
View File

@@ -0,0 +1,152 @@
package cryptor
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestAesEcbEncrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefghijklmnop"
aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key))
aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesEcbEncrypt")
assert.Equal(data, string(aesEcbDecrypt))
}
func TestAesCbcEncrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefghijklmnop"
aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key))
aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesCbcEncrypt")
assert.Equal(data, string(aesCbcDecrypt))
}
func TestAesCtrCrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefghijklmnop"
aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key))
aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesCtrCrypt")
assert.Equal(data, string(aesCtrDeCrypt))
}
func TestAesCfbEncrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefghijklmnop"
aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key))
aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesCfbEncrypt")
assert.Equal(data, string(aesCfbDecrypt))
}
func TestAesOfbEncrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefghijklmnop"
aesOfbEncrypt := AesOfbEncrypt([]byte(data), []byte(key))
aesOfbDecrypt := AesOfbDecrypt(aesOfbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesOfbEncrypt")
assert.Equal(data, string(aesOfbDecrypt))
}
func TestDesEcbEncrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefgh"
desEcbEncrypt := DesEcbEncrypt([]byte(data), []byte(key))
desEcbDecrypt := DesEcbDecrypt(desEcbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesEcbEncrypt")
assert.Equal(data, string(desEcbDecrypt))
}
func TestDesCbcEncrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefgh"
desCbcEncrypt := DesCbcEncrypt([]byte(data), []byte(key))
desCbcDecrypt := DesCbcDecrypt(desCbcEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesCbcEncrypt")
assert.Equal(data, string(desCbcDecrypt))
}
func TestDesCtrCrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefgh"
desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key))
desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesCtrCrypt")
assert.Equal(data, string(desCtrDeCrypt))
}
func TestDesCfbEncrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefgh"
desCfbEncrypt := DesCfbEncrypt([]byte(data), []byte(key))
desCfbDecrypt := DesCfbDecrypt(desCfbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesCfbEncrypt")
assert.Equal(data, string(desCfbDecrypt))
}
func TestDesOfbEncrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefgh"
desOfbEncrypt := DesOfbEncrypt([]byte(data), []byte(key))
desOfbDecrypt := DesOfbDecrypt(desOfbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesOfbEncrypt")
assert.Equal(data, string(desOfbDecrypt))
}
func TestRsaEncrypt(t *testing.T) {
t.Parallel()
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
if err != nil {
t.FailNow()
}
data := []byte("hello world")
encrypted := RsaEncrypt(data, "rsa_public.pem")
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
assert := internal.NewAssert(t, "TestRsaEncrypt")
assert.Equal(string(data), string(decrypted))
}

View File

@@ -1,173 +0,0 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package cryptor implements some util functions to encrypt and decrypt.
package cryptor
import (
"bytes"
"crypto/cipher"
"crypto/des"
"crypto/rand"
"io"
)
// DesEcbEncrypt encrypt data with key use DES ECB algorithm
// len(key) should be 8
func DesEcbEncrypt(data, key []byte) []byte {
cipher, _ := des.NewCipher(generateDesKey(key))
length := (len(data) + des.BlockSize) / des.BlockSize
plain := make([]byte, length*des.BlockSize)
copy(plain, data)
pad := byte(len(plain) - len(data))
for i := len(data); i < len(plain); i++ {
plain[i] = pad
}
encrypted := make([]byte, len(plain))
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
}
return encrypted
}
// DesEcbDecrypt decrypt data with key use DES ECB algorithm
// len(key) should be 8
func DesEcbDecrypt(encrypted, key []byte) []byte {
cipher, _ := des.NewCipher(generateDesKey(key))
decrypted := make([]byte, len(encrypted))
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
}
trim := 0
if len(decrypted) > 0 {
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
}
return decrypted[:trim]
}
// DesCbcEncrypt encrypt data with key use DES CBC algorithm
// len(key) should be 8
func DesCbcEncrypt(data, key []byte) []byte {
block, _ := des.NewCipher(key)
blockSize := block.BlockSize()
data = pkcs7Padding(data, blockSize)
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
encrypted := make([]byte, len(data))
blockMode.CryptBlocks(encrypted, data)
return encrypted
}
// DesCbcDecrypt decrypt data with key use DES CBC algorithm
// len(key) should be 8
func DesCbcDecrypt(encrypted, key []byte) []byte {
block, _ := des.NewCipher(key)
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
decrypted := make([]byte, len(encrypted))
blockMode.CryptBlocks(decrypted, encrypted)
decrypted = pkcs7UnPadding(decrypted)
return decrypted
}
// DesCtrCrypt encrypt data with key use DES CTR algorithm
// len(key) should be 8
func DesCtrCrypt(data, key []byte) []byte {
block, _ := des.NewCipher(key)
iv := bytes.Repeat([]byte("1"), block.BlockSize())
stream := cipher.NewCTR(block, iv)
dst := make([]byte, len(data))
stream.XORKeyStream(dst, data)
return dst
}
// DesCfbEncrypt encrypt data with key use DES CFB algorithm
// len(key) should be 8
func DesCfbEncrypt(data, key []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}
encrypted := make([]byte, des.BlockSize+len(data))
iv := encrypted[:des.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(encrypted[des.BlockSize:], data)
return encrypted
}
// DesCfbDecrypt decrypt data with key use DES CFB algorithm
// len(encrypted) should be great than 16, len(key) should be 8
func DesCfbDecrypt(encrypted, key []byte) []byte {
block, _ := des.NewCipher(key)
if len(encrypted) < des.BlockSize {
panic("encrypted data is too short")
}
iv := encrypted[:des.BlockSize]
encrypted = encrypted[des.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(encrypted, encrypted)
return encrypted
}
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
// len(key) should be 16, 24 or 32
func DesOfbEncrypt(data, key []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}
data = pkcs7Padding(data, des.BlockSize)
encrypted := make([]byte, des.BlockSize+len(data))
iv := encrypted[:des.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(encrypted[des.BlockSize:], data)
return encrypted
}
// DesOfbDecrypt decrypt data with key use DES OFB algorithm
// len(key) should be 8
func DesOfbDecrypt(data, key []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}
iv := data[:des.BlockSize]
data = data[des.BlockSize:]
if len(data)%des.BlockSize != 0 {
return nil
}
decrypted := make([]byte, len(data))
mode := cipher.NewOFB(block, iv)
mode.XORKeyStream(decrypted, data)
decrypted = pkcs7UnPadding(decrypted)
return decrypted
}

View File

@@ -1,62 +0,0 @@
package cryptor
import (
"testing"
"github.com/duke-git/lancet/internal"
)
func TestDesEcbEncrypt(t *testing.T) {
data := "hello world"
key := "abcdefgh"
desEcbEncrypt := DesEcbEncrypt([]byte(data), []byte(key))
desEcbDecrypt := DesEcbDecrypt(desEcbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesEcbEncrypt")
assert.Equal(data, string(desEcbDecrypt))
}
func TestDesCbcEncrypt(t *testing.T) {
data := "hello world"
key := "abcdefgh"
desCbcEncrypt := DesCbcEncrypt([]byte(data), []byte(key))
desCbcDecrypt := DesCbcDecrypt(desCbcEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesCbcEncrypt")
assert.Equal(data, string(desCbcDecrypt))
}
func TestDesCtrCrypt(t *testing.T) {
data := "hello world"
key := "abcdefgh"
desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key))
desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesCtrCrypt")
assert.Equal(data, string(desCtrDeCrypt))
}
func TestDesCfbEncrypt(t *testing.T) {
data := "hello world"
key := "abcdefgh"
desCfbEncrypt := DesCfbEncrypt([]byte(data), []byte(key))
desCfbDecrypt := DesCfbDecrypt(desCfbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesCfbEncrypt")
assert.Equal(data, string(desCfbDecrypt))
}
func TestDesOfbEncrypt(t *testing.T) {
data := "hello world"
key := "abcdefgh"
desOfbEncrypt := DesOfbEncrypt([]byte(data), []byte(key))
desOfbDecrypt := DesOfbDecrypt(desOfbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesOfbEncrypt")
assert.Equal(data, string(desOfbDecrypt))
}

View File

@@ -1,118 +0,0 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package cryptor implements some util functions to encrypt and decrypt.
package cryptor
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"os"
)
// GenerateRsaKey make a rsa private key, and return key file name
// Generated key file is `rsa_private.pem` and `rsa_public.pem` in current path
func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
// private key
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
return err
}
derText := x509.MarshalPKCS1PrivateKey(privateKey)
block := pem.Block{
Type: "rsa private key",
Bytes: derText,
}
//file,err := os.Create("rsa_private.pem")
file, err := os.Create(priKeyFile)
if err != nil {
panic(err)
}
pem.Encode(file, &block)
file.Close()
// public key
publicKey := privateKey.PublicKey
derpText, err := x509.MarshalPKIXPublicKey(&publicKey)
if err != nil {
return err
}
block = pem.Block{
Type: "rsa public key",
Bytes: derpText,
}
//file,err = os.Create("rsa_public.pem")
file, err = os.Create(pubKeyFile)
if err != nil {
return err
}
pem.Encode(file, &block)
file.Close()
return nil
}
// RsaEncrypt encrypt data with ras algorithm
func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
file, err := os.Open(pubKeyFileName)
if err != nil {
panic(err)
}
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
defer file.Close()
buf := make([]byte, fileInfo.Size())
file.Read(buf)
block, _ := pem.Decode(buf)
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
panic(err)
}
pubKey := pubInterface.(*rsa.PublicKey)
cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, data)
if err != nil {
panic(err)
}
return cipherText
}
// RsaDecrypt decrypt data with ras algorithm
func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
file, err := os.Open(privateKeyFileName)
if err != nil {
panic(err)
}
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, fileInfo.Size())
defer file.Close()
file.Read(buf)
block, _ := pem.Decode(buf)
priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
panic(err)
}
plainText, err := rsa.DecryptPKCS1v15(rand.Reader, priKey, data)
if err != nil {
panic(err)
}
return plainText
}

View File

@@ -1,20 +0,0 @@
package cryptor
import (
"testing"
"github.com/duke-git/lancet/internal"
)
func TestRsaEncrypt(t *testing.T) {
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
if err != nil {
t.FailNow()
}
data := []byte("hello world")
encrypted := RsaEncrypt(data, "rsa_public.pem")
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
assert := internal.NewAssert(t, "TestRsaEncrypt")
assert.Equal(string(data), string(decrypted))
}

View File

@@ -0,0 +1,193 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure implements some data structure. hashmap structure.
package datastructure
import (
"fmt"
"hash/fnv"
"reflect"
)
var defaultMapCapacity uint64 = 1 << 10
type mapNode struct {
key any
value any
next *mapNode
}
// HashMap implements a hash map
type HashMap struct {
capacity uint64
size uint64
table []*mapNode
}
// NewHashMap return a HashMap instance
func NewHashMap() *HashMap {
return &HashMap{
capacity: defaultMapCapacity,
table: make([]*mapNode, defaultMapCapacity),
}
}
// NewHashMapWithCapacity return a HashMap instance with given size and capacity
func NewHashMapWithCapacity(size, capacity uint64) *HashMap {
return &HashMap{
size: size,
capacity: capacity,
table: make([]*mapNode, capacity),
}
}
// Get return the value of given key in hashmap
func (hm *HashMap) Get(key any) any {
hashValue := hm.hash(key)
node := hm.table[hashValue]
for node != nil {
if reflect.DeepEqual(node.key, key) {
return node.value
}
node = node.next
}
return nil
}
// GetOrDefault return the value of given key in hashmap, if not found return default value
func (hm *HashMap) GetOrDefault(key any, defaultValue any) any {
value := hm.Get(key)
if value == nil {
return defaultValue
}
return value
}
// Put new key value in hashmap
func (hm *HashMap) Put(key any, value any) {
hm.putValue(hm.hash(key), key, value)
}
func (hm *HashMap) putValue(hash uint64, key, value any) {
if hm.capacity == 0 {
hm.capacity = defaultMapCapacity
hm.table = make([]*mapNode, defaultMapCapacity)
}
node := hm.table[hash]
if node == nil {
hm.table[hash] = newMapNode(key, value)
} else if node.key == key {
hm.table[hash] = newMapNodeWithNext(key, value, node)
} else {
hm.resize()
hm.putValue(hash, value, value)
}
hm.size++
}
// Delete item by given key in hashmap
func (hm *HashMap) Delete(key any) {
hash := hm.hash(key)
node := hm.table[hash]
if node == nil {
return
}
hm.table = append(hm.table[:hash], hm.table[hash+1:]...)
hm.size--
}
// Contains checks if given key is in hashmap or not
func (hm *HashMap) Contains(key any) bool {
node := hm.table[hm.hash(key)]
for node != nil {
if reflect.DeepEqual(node.key, key) {
return true
}
node = node.next
}
return false
}
// Iterate executes iteratee funcation for every key and value pair of hashmap (random order)
func (hm *HashMap) Iterate(iteratee func(key, value any)) {
if hm.size > 0 {
for i := 0; i < len(hm.table); i++ {
item := hm.table[i]
if item != nil {
iteratee(item.key, item.value)
}
}
}
}
// Keys returns a slice of the hashmap's keys (random order)
func (hm *HashMap) Keys() []any {
keys := make([]any, int(hm.size))
index := 0
if hm.size > 0 {
hm.Iterate(func(key, value any) {
keys[index] = key
index++
})
}
return keys
}
// Values returns a slice of the hashmap's keys (random order)
func (hm *HashMap) Values() []any {
values := make([]any, int(hm.size))
index := 0
if hm.size > 0 {
hm.Iterate(func(key, value any) {
values[index] = value
index++
})
}
return values
}
func (hm *HashMap) resize() {
hm.capacity <<= 1
tempTable := hm.table
hm.table = make([]*mapNode, hm.capacity)
for i := 0; i < len(tempTable); i++ {
node := tempTable[i]
if node == nil {
continue
}
hm.table[hm.hash(node.key)] = node
}
}
func (hm *HashMap) hash(key any) uint64 {
h := fnv.New64a()
_, _ = h.Write([]byte(fmt.Sprintf("%v", key)))
hashValue := h.Sum64()
return (hm.capacity - 1) & (hashValue ^ (hashValue >> 16))
}
func newMapNode(key, value any) *mapNode {
return &mapNode{
key: key,
value: value,
}
}
func newMapNodeWithNext(key, value any, next *mapNode) *mapNode {
return &mapNode{
key: key,
value: value,
next: next,
}
}

View File

@@ -0,0 +1,107 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestHashMap_PutAndGet(t *testing.T) {
assert := internal.NewAssert(t, "TestHashMap_PutAndGet")
hm := NewHashMap()
hm.Put("abc", 3)
assert.Equal(3, hm.Get("abc"))
assert.IsNil(hm.Get("abcd"))
hm.Put("abc", 4)
assert.Equal(4, hm.Get("abc"))
}
func TestHashMap_Resize(t *testing.T) {
assert := internal.NewAssert(t, "TestHashMap_Resize")
hm := NewHashMapWithCapacity(3, 3)
for i := 0; i < 20; i++ {
hm.Put(i, 10)
}
assert.Equal(10, hm.Get(5))
}
func TestHashMap_Delete(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHashMap_Delete")
hm := NewHashMap()
hm.Put("abc", 3)
assert.Equal(3, hm.Get("abc"))
hm.Delete("abc")
assert.IsNil(hm.Get("abc"))
}
func TestHashMap_Contains(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHashMap_Contains")
hm := NewHashMap()
assert.Equal(false, hm.Contains("abc"))
hm.Put("abc", 3)
assert.Equal(true, hm.Contains("abc"))
}
func TestHashMap_KeysValues(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHashMap_KeysValues")
hm := NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
keys := hm.Keys()
values := hm.Values()
assert.Equal(3, len(values))
assert.Equal(3, len(keys))
}
func TestHashMap_Keys(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHashMap_Keys")
hm := NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
keys := hm.Keys()
assert.Equal(3, len(keys))
}
func TestHashMap_GetOrDefault(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHashMap_GetOrDefault")
hm := NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
assert.Equal(1, hm.GetOrDefault("a", 5))
assert.Equal(5, hm.GetOrDefault("d", 5))
}

View File

@@ -0,0 +1,203 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure implements some data structure. MaxHeap is a binary max heap.
package datastructure
import (
"fmt"
"github.com/duke-git/lancet/v2/lancetconstraints"
)
// MaxHeap implements a binary max heap
// type T should implements Compare function in lancetconstraints.Comparator interface.
type MaxHeap[T any] struct {
data []T
comparator lancetconstraints.Comparator
}
// NewMaxHeap returns a MaxHeap instance with the given comparator.
func NewMaxHeap[T any](comparator lancetconstraints.Comparator) *MaxHeap[T] {
return &MaxHeap[T]{
data: make([]T, 0),
comparator: comparator,
}
}
// BuildMaxHeap builds a MaxHeap instance with data and given comparator.
func BuildMaxHeap[T any](data []T, comparator lancetconstraints.Comparator) *MaxHeap[T] {
heap := &MaxHeap[T]{
data: make([]T, 0, len(data)),
comparator: comparator,
}
for _, v := range data {
heap.Push(v)
}
return heap
}
// Push value into the heap
func (h *MaxHeap[T]) Push(value T) {
h.data = append(h.data, value)
h.heapifyUp(len(h.data) - 1)
}
// heapifyUp heapify the data from bottom to top
func (h *MaxHeap[T]) heapifyUp(i int) {
for h.comparator.Compare(h.data[parentIndex(i)], h.data[i]) < 0 {
h.swap(parentIndex(i), i)
i = parentIndex(i)
}
}
// Pop return the largest value, and remove it from the heap
// if heap is empty, return zero value and fasle
func (h *MaxHeap[T]) Pop() (T, bool) {
var val T
if h.Size() == 0 {
return val, false
}
val = h.data[0]
l := len(h.data) - 1
h.data[0] = h.data[l]
h.data = h.data[:l]
h.heapifyDown(0)
return val, true
}
// heapifyDown heapify the data from top to bottom
func (h *MaxHeap[T]) heapifyDown(i int) {
lastIndex := len(h.data) - 1
l, r := leftChildIndex(i), rightChildIndex(i)
childToCompare := 0
for l <= lastIndex {
if l == lastIndex {
childToCompare = l
} else if h.comparator.Compare(h.data[l], h.data[r]) > 0 {
childToCompare = l
} else {
childToCompare = r
}
if h.comparator.Compare(h.data[i], h.data[childToCompare]) < 0 {
h.swap(i, childToCompare)
i = childToCompare
l, r = leftChildIndex(i), rightChildIndex(i)
} else {
break
}
}
}
// Peek returns the largest element from the heap without removing it.
// if heap is empty, it returns zero value and false.
func (h *MaxHeap[T]) Peek() (T, bool) {
if h.Size() == 0 {
var val T
return val, false
}
return h.data[0], true
}
// Size return the number of elements in the heap
func (h *MaxHeap[T]) Size() int {
return len(h.data)
}
// Data return data of the heap
func (h *MaxHeap[T]) Data() []T {
return h.data
}
// PrintStructure print the structure of the heap
func (h *MaxHeap[T]) PrintStructure() {
level := 1
data := h.data
length := len(h.data)
index := 0
list := [][]string{}
temp := []string{}
for index < length {
start := powerTwo(level-1) - 1
end := start + powerTwo(level-1) - 1
temp = append(temp, fmt.Sprintf("%v", data[index]))
index++
if index > end || index >= length {
list = append(list, temp)
temp = []string{}
if index < length {
level++
}
}
}
lastNum := powerTwo(level - 1)
lastLen := lastNum + (lastNum - 1)
heapTree := make([][]string, level)
for i := 0; i < level; i++ {
heapTree[i] = make([]string, lastLen)
for j := 0; j < lastLen; j++ {
heapTree[i][j] = ""
}
}
for k := 0; k < len(list); k++ {
vals := list[k]
tempLevel := level - k
st := powerTwo(tempLevel-1) - 1
for _, v := range vals {
heapTree[k][st] = v
gap := powerTwo(tempLevel)
st = st + gap
}
}
for m := 0; m < level; m++ {
for n := 0; n < lastLen; n++ {
val := heapTree[m][n]
if val == "" {
fmt.Print(" ")
} else {
fmt.Print(val)
}
}
fmt.Println()
}
}
// parentIndex get parent index of the given index
func parentIndex(i int) int {
return (i - 1) / 2
}
// leftChildIndex get left child index of the given index
func leftChildIndex(i int) int {
return 2*i + 1
}
// rightChildIndex get right child index of the given index
func rightChildIndex(i int) int {
return 2*i + 2
}
// swap two elements in the heap
func (h *MaxHeap[T]) swap(i, j int) {
h.data[i], h.data[j] = h.data[j], h.data[i]
}
func powerTwo(n int) int {
return 1 << n
}

View File

@@ -0,0 +1,98 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func TestMaxHeap_BuildMaxHeap(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMaxHeap_BuildMaxHeap")
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
heap := BuildMaxHeap(values, &intComparator{})
expected := []int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2}
assert.Equal(expected, heap.data)
assert.Equal(12, heap.Size())
}
func TestMaxHeap_Push(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMaxHeap_Push")
heap := NewMaxHeap[int](&intComparator{})
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
for _, v := range values {
heap.Push(v)
}
expected := []int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2}
assert.Equal(expected, heap.data)
assert.Equal(12, heap.Size())
heap.PrintStructure()
}
func TestMaxHeap_Pop(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMaxHeap_Pop")
heap := NewMaxHeap[int](&intComparator{})
_, ok := heap.Pop()
assert.Equal(false, ok)
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
for _, v := range values {
heap.Push(v)
}
val, ok := heap.Pop()
assert.Equal(12, val)
assert.Equal(true, ok)
assert.Equal(11, heap.Size())
}
func TestMaxHeap_Peek(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMaxHeap_Peek")
heap := NewMaxHeap[int](&intComparator{})
_, ok := heap.Peek()
assert.Equal(false, ok)
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
for _, v := range values {
heap.Push(v)
}
val, ok := heap.Peek()
assert.Equal(12, val)
assert.Equal(true, ok)
assert.Equal(12, heap.Size())
}

View File

@@ -0,0 +1,242 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure. Link structure contains SinglyLink and DoublyLink.
package datastructure
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure"
)
// DoublyLink is a linked list. Whose node has a generic Value, Pre pointer points to a previous node of the dl, Next pointer points to a next node of the dl.
type DoublyLink[T any] struct {
Head *datastructure.LinkNode[T]
length int
}
// NewDoublyLink return *DoublyLink instance
func NewDoublyLink[T any]() *DoublyLink[T] {
return &DoublyLink[T]{Head: nil}
}
// InsertAtHead insert value into doubly linklist at head index
func (dl *DoublyLink[T]) InsertAtHead(value T) {
newNode := datastructure.NewLinkNode(value)
size := dl.Size()
if size == 0 {
dl.Head = newNode
dl.length++
return
}
newNode.Next = dl.Head
newNode.Pre = nil
dl.Head.Pre = newNode
dl.Head = newNode
dl.length++
}
// InsertAtTail insert value into doubly linklist at tail index
func (dl *DoublyLink[T]) InsertAtTail(value T) {
current := dl.Head
if current == nil {
dl.InsertAtHead(value)
return
}
for current.Next != nil {
current = current.Next
}
newNode := datastructure.NewLinkNode(value)
newNode.Next = nil
newNode.Pre = current
current.Next = newNode
dl.length++
}
// InsertAt insert value into doubly linklist at index
// param `index` should between [0, length], if index do not meet the conditions, do nothing
func (dl *DoublyLink[T]) InsertAt(index int, value T) {
size := dl.length
if index < 0 || index > size {
return
}
if index == 0 {
dl.InsertAtHead(value)
return
}
if index == size {
dl.InsertAtTail(value)
return
}
i := 0
current := dl.Head
for current != nil {
if i == index-1 {
newNode := datastructure.NewLinkNode(value)
newNode.Next = current.Next
newNode.Pre = current
current.Next = newNode
dl.length++
return
}
i++
current = current.Next
}
}
// DeleteAtHead delete value in doubly linklist at head index
func (dl *DoublyLink[T]) DeleteAtHead() {
if dl.Head == nil {
return
}
current := dl.Head
dl.Head = current.Next
dl.Head.Pre = nil
dl.length--
}
// DeleteAtTail delete value in doubly linklist at tail
func (dl *DoublyLink[T]) DeleteAtTail() {
if dl.Head == nil {
return
}
current := dl.Head
if current.Next == nil {
dl.DeleteAtHead()
}
for current.Next.Next != nil {
current = current.Next
}
current.Next = nil
dl.length--
}
// DeleteAt delete value in doubly linklist at index
// param `index` should be [0, len(DoublyLink)-1]
func (dl *DoublyLink[T]) DeleteAt(index int) {
if dl.Head == nil {
return
}
current := dl.Head
if current.Next == nil || index == 0 {
dl.DeleteAtHead()
}
if index == dl.length-1 {
dl.DeleteAtTail()
}
if index < 0 || index > dl.length-1 {
return
}
i := 0
for current != nil {
if i == index-1 {
current.Next = current.Next.Next
dl.length--
return
}
i++
current = current.Next
}
}
// Reverse the linked list
func (dl *DoublyLink[T]) Reverse() {
current := dl.Head
var temp *datastructure.LinkNode[T]
for current != nil {
temp = current.Pre
current.Pre = current.Next
current.Next = temp
current = current.Pre
}
if temp != nil {
dl.Head = temp.Pre
}
}
// GetMiddleNode return node at middle index of linked list
func (dl *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
if dl.Head == nil {
return nil
}
if dl.Head.Next == nil {
return dl.Head
}
fast := dl.Head
slow := dl.Head
for fast != nil {
fast = fast.Next
if fast != nil {
fast = fast.Next
slow = slow.Next
} else {
return slow
}
}
return slow
}
// Size return the count of doubly linked list
func (dl *DoublyLink[T]) Size() int {
return dl.length
}
// Values return slice of all doubly linklist node value
func (dl *DoublyLink[T]) Values() []T {
result := []T{}
current := dl.Head
for current != nil {
result = append(result, current.Value)
current = current.Next
}
return result
}
// Print all nodes info of a linked list
func (dl *DoublyLink[T]) Print() {
current := dl.Head
info := "[ "
for current != nil {
info += fmt.Sprintf("%+v, ", current)
current = current.Next
}
info += " ]"
fmt.Println(info)
}
// IsEmpty checks if dl is empty or not
func (dl *DoublyLink[T]) IsEmpty() bool {
return dl.length == 0
}
// Clear all nodes in doubly linklist
func (dl *DoublyLink[T]) Clear() {
dl.Head = nil
dl.length = 0
}

View File

@@ -0,0 +1,181 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestDoublyLink_InsertAtFirst(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDoublyLink_InsertAtFirst")
link := NewDoublyLink[int]()
link.InsertAtHead(1)
link.InsertAtHead(2)
link.InsertAtHead(3)
link.Print()
expected := []int{3, 2, 1}
values := link.Values()
assert.Equal(expected, values)
}
func TestDoublyLink_InsertAtTail(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDoublyLink_InsertAtTail")
link := NewDoublyLink[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.Print()
expected := []int{1, 2, 3}
values := link.Values()
assert.Equal(expected, values)
}
func TestDoublyLink_InsertAt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDoublyLink_InsertAt")
link := NewDoublyLink[int]()
link.InsertAt(1, 1) //do nothing
link.InsertAt(0, 1)
link.InsertAt(1, 2)
link.InsertAt(2, 4)
link.InsertAt(2, 3)
expected := []int{1, 2, 3, 4}
values := link.Values()
assert.Equal(expected, values)
}
func TestDoublyLink_DeleteAtHead(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtHead")
link := NewDoublyLink[int]()
link.DeleteAtHead()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
link.DeleteAtHead()
expected := []int{2, 3, 4}
values := link.Values()
assert.Equal(expected, values)
}
func TestDoublyLink_DeleteAtTail(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtTail")
link := NewDoublyLink[int]()
link.DeleteAtTail()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
link.DeleteAtTail()
expected := []int{1, 2, 3}
values := link.Values()
assert.Equal(expected, values)
}
func TestDoublyLink_DeleteAt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAt")
link := NewDoublyLink[int]()
link.DeleteAt(0)
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
link.InsertAtTail(5)
link.DeleteAt(0)
assert.Equal([]int{2, 3, 4, 5}, link.Values())
link.DeleteAt(3)
assert.Equal([]int{2, 3, 4}, link.Values())
link.DeleteAt(1)
assert.Equal(2, link.Size())
assert.Equal([]int{2, 4}, link.Values())
}
func TestDoublyLink_Reverse(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDoublyLink_Reverse")
link := NewDoublyLink[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
link.Reverse()
link.Print()
assert.Equal([]int{4, 3, 2, 1}, link.Values())
}
func TestDoublyLink_GetMiddleNode(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDoublyLink_GetMiddleNode")
link := NewDoublyLink[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
middle1 := link.GetMiddleNode()
assert.Equal(3, middle1.Value)
link.InsertAtTail(5)
link.InsertAtTail(6)
link.InsertAtTail(7)
middle2 := link.GetMiddleNode()
assert.Equal(4, middle2.Value)
}
func TestDoublyLink_Clear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDoublyLink_Clear")
link := NewDoublyLink[int]()
assert.Equal(true, link.IsEmpty())
assert.Equal(0, link.Size())
link.InsertAtTail(1)
assert.Equal(false, link.IsEmpty())
assert.Equal(1, link.Size())
link.Clear()
assert.Equal(true, link.IsEmpty())
assert.Equal(0, link.Size())
}

View File

@@ -0,0 +1,245 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure. Link structure contains SinglyLink and DoublyLink.
package datastructure
import (
"fmt"
"reflect"
"github.com/duke-git/lancet/v2/datastructure"
)
// SinglyLink is a linked list. Whose node has a Value generics and Next pointer points to a next node of the sl.
type SinglyLink[T any] struct {
Head *datastructure.LinkNode[T]
length int
}
// NewSinglyLink return *SinglyLink instance
func NewSinglyLink[T any]() *SinglyLink[T] {
return &SinglyLink[T]{Head: nil}
}
// InsertAtHead insert value into singly linklist at head index
func (sl *SinglyLink[T]) InsertAtHead(value T) {
newNode := datastructure.NewLinkNode(value)
newNode.Next = sl.Head
sl.Head = newNode
sl.length++
}
// InsertAtTail insert value into singly linklist at tail index
func (sl *SinglyLink[T]) InsertAtTail(value T) {
current := sl.Head
if current == nil {
sl.InsertAtHead(value)
return
}
for current.Next != nil {
current = current.Next
}
newNode := datastructure.NewLinkNode(value)
newNode.Next = nil
current.Next = newNode
sl.length++
}
// InsertAt insert value into singly linklist at index
// param `index` should between [0, len(SinglyLink)], if index do not meet the conditions, do nothing
func (sl *SinglyLink[T]) InsertAt(index int, value T) {
size := sl.length
if index < 0 || index > size {
return
}
if index == 0 {
sl.InsertAtHead(value)
return
}
if index == size {
sl.InsertAtTail(value)
return
}
i := 0
current := sl.Head
for current != nil {
if i == index-1 {
newNode := datastructure.NewLinkNode(value)
newNode.Next = current.Next
current.Next = newNode
sl.length++
return
}
i++
current = current.Next
}
}
// DeleteAtHead delete value in singly linklist at head index
func (sl *SinglyLink[T]) DeleteAtHead() {
if sl.Head == nil {
return
}
current := sl.Head
sl.Head = current.Next
sl.length--
}
// DeleteAtTail delete value in singly linklist at tail
func (sl *SinglyLink[T]) DeleteAtTail() {
if sl.Head == nil {
return
}
current := sl.Head
if current.Next == nil {
sl.DeleteAtHead()
}
for current.Next.Next != nil {
current = current.Next
}
current.Next = nil
sl.length--
}
// DeleteAt delete value in singly linklist at index
// param `index` should be [0, len(SinglyLink)-1]
func (sl *SinglyLink[T]) DeleteAt(index int) {
if sl.Head == nil {
return
}
current := sl.Head
if current.Next == nil || index == 0 {
sl.DeleteAtHead()
}
if index == sl.length-1 {
sl.DeleteAtTail()
}
if index < 0 || index > sl.length-1 {
return
}
i := 0
for current != nil {
if i == index-1 {
current.Next = current.Next.Next
sl.length--
return
}
i++
current = current.Next
}
}
// DeleteValue delete value in singly linklist
func (sl *SinglyLink[T]) DeleteValue(value T) {
if sl.Head == nil {
return
}
dummyHead := datastructure.NewLinkNode(value)
dummyHead.Next = sl.Head
current := dummyHead
for current.Next != nil {
if reflect.DeepEqual(current.Next.Value, value) {
current.Next = current.Next.Next
sl.length--
} else {
current = current.Next
}
}
sl.Head = dummyHead.Next
}
// Reverse the linked list
func (sl *SinglyLink[T]) Reverse() {
var pre, next *datastructure.LinkNode[T]
current := sl.Head
for current != nil {
next = current.Next
current.Next = pre
pre = current
current = next
}
sl.Head = pre
}
// GetMiddleNode return node at middle index of linked list
func (sl *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
if sl.Head == nil {
return nil
}
if sl.Head.Next == nil {
return sl.Head
}
fast := sl.Head
slow := sl.Head
for fast != nil {
fast = fast.Next
if fast != nil {
fast = fast.Next
slow = slow.Next
} else {
return slow
}
}
return slow
}
// Size return the count of singly linked list
func (sl *SinglyLink[T]) Size() int {
return sl.length
}
// Values return slice of all singly linklist node value
func (sl *SinglyLink[T]) Values() []T {
result := []T{}
current := sl.Head
for current != nil {
result = append(result, current.Value)
current = current.Next
}
return result
}
// IsEmpty checks if sl is empty or not
func (sl *SinglyLink[T]) IsEmpty() bool {
return sl.length == 0
}
// Clear all the node in singly linklist
func (sl *SinglyLink[T]) Clear() {
sl.Head = nil
sl.length = 0
}
// Print all nodes info of a linked list
func (sl *SinglyLink[T]) Print() {
current := sl.Head
info := "[ "
for current != nil {
info += fmt.Sprintf("%+v, ", current)
current = current.Next
}
info += " ]"
fmt.Println(info)
}

View File

@@ -0,0 +1,201 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestSinglyLink_InsertAtFirst(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSinglyLink_InsertAtFirst")
link := NewSinglyLink[int]()
link.InsertAtHead(1)
link.InsertAtHead(2)
link.InsertAtHead(3)
link.Print()
expected := []int{3, 2, 1}
values := link.Values()
assert.Equal(expected, values)
}
func TestSinglyLink_InsertAtTail(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSinglyLink_InsertAtTail")
link := NewSinglyLink[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.Print()
expected := []int{1, 2, 3}
values := link.Values()
assert.Equal(expected, values)
}
func TestSinglyLink_InsertAt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSinglyLink_InsertAt")
link := NewSinglyLink[int]()
link.InsertAt(1, 1) //do nothing
link.InsertAt(0, 1)
link.InsertAt(1, 2)
link.InsertAt(2, 4)
link.InsertAt(2, 3)
link.Print()
expected := []int{1, 2, 3, 4}
values := link.Values()
assert.Equal(expected, values)
}
func TestSinglyLink_DeleteAtHead(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtHead")
link := NewSinglyLink[int]()
link.DeleteAtHead()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
link.DeleteAtHead()
link.Print()
expected := []int{2, 3, 4}
values := link.Values()
assert.Equal(expected, values)
}
func TestSinglyLink_DeleteAtTail(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtTail")
link := NewSinglyLink[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
link.DeleteAtTail()
expected := []int{1, 2, 3}
values := link.Values()
assert.Equal(expected, values)
}
func TestSinglyLink_DeleteValue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSinglyLink_DeleteValue")
link := NewSinglyLink[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
link.DeleteValue(2)
assert.Equal([]int{1, 3, 4}, link.Values())
link.DeleteValue(1)
assert.Equal([]int{3, 4}, link.Values())
}
func TestSinglyLink_DeleteAt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAt")
link := NewSinglyLink[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
link.InsertAtTail(5)
link.DeleteAt(0)
assert.Equal([]int{2, 3, 4, 5}, link.Values())
link.DeleteAt(3)
assert.Equal([]int{2, 3, 4}, link.Values())
link.DeleteAt(1)
assert.Equal(2, link.Size())
assert.Equal([]int{2, 4}, link.Values())
}
func TestSinglyLink_Reverse(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSinglyLink_Reverse")
link := NewSinglyLink[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
link.Reverse()
assert.Equal([]int{4, 3, 2, 1}, link.Values())
}
func TestSinglyLink_GetMiddleNode(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSinglyLink_GetMiddleNode")
link := NewSinglyLink[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.InsertAtTail(4)
middle1 := link.GetMiddleNode()
assert.Equal(3, middle1.Value)
link.InsertAtTail(5)
link.InsertAtTail(6)
link.InsertAtTail(7)
middle2 := link.GetMiddleNode()
assert.Equal(4, middle2.Value)
}
func TestSinglyLink_Clear(t *testing.T) {
assert := internal.NewAssert(t, "TestSinglyLink_Clear")
link := NewSinglyLink[int]()
assert.Equal(true, link.IsEmpty())
assert.Equal(0, link.Size())
link.InsertAtTail(1)
assert.Equal(false, link.IsEmpty())
assert.Equal(1, link.Size())
link.Clear()
assert.Equal(true, link.IsEmpty())
assert.Equal(0, link.Size())
}

View File

@@ -0,0 +1,350 @@
package datastructure
import (
"reflect"
"sort"
"sync"
)
type CopyOnWriteList[T any] struct {
data []T
lock sync.Locker
}
// NewCopyOnWriteList Creates an empty list.
func NewCopyOnWriteList[T any](data []T) *CopyOnWriteList[T] {
return &CopyOnWriteList[T]{data: data, lock: &sync.RWMutex{}}
}
func (c *CopyOnWriteList[T]) getList() []T {
return c.data
}
func (c *CopyOnWriteList[T]) setList(data []T) {
c.data = data
}
// Size returns the number of elements in this list.
func (c *CopyOnWriteList[T]) Size() int {
return len(c.getList())
}
// IsEmpty returns true if this list contains no elements.
func (c *CopyOnWriteList[T]) IsEmpty() bool {
return c.Size() == 0
}
// Contain returns true if this list contains the specified element.
func (c *CopyOnWriteList[T]) Contain(e T) bool {
list := c.getList()
return indexOf(e, list, 0, c.Size()) >= 0
}
// ValueOf returns the index of the first occurrence of the specified element in this list, or null if this list does not contain the element.
func (c *CopyOnWriteList[T]) ValueOf(index int) (*T, bool) {
list := c.getList()
if index < 0 || index >= len(c.data) {
return nil, false
}
return get(list, index), true
}
// IndexOf returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
func (c *CopyOnWriteList[T]) IndexOf(e T) int {
list := c.getList()
return indexOf(e, list, 0, c.Size())
}
// indexOf returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
// start the start position of the search (inclusive)
// end the end position of the search (exclusive)
func indexOf[T any](o T, e []T, start int, end int) int {
if start >= end {
return -1
}
for i := start; i < end; i++ {
if reflect.DeepEqual(e[i], o) {
return i
}
}
return -1
}
// LastIndexOf returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
func (c *CopyOnWriteList[T]) LastIndexOf(e T) int {
list := c.getList()
return lastIndexOf(e, list, 0, c.Size())
}
// lastIndexOf returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
// start the start position of the search (inclusive)
// end the end position of the search (exclusive)
func lastIndexOf[T any](o T, e []T, start int, end int) int {
if start >= end {
return -1
}
for i := end - 1; i >= start; i-- {
if reflect.DeepEqual(e[i], o) {
return i
}
}
return -1
}
// get returns the element at the specified position in this list.
func get[T any](o []T, index int) *T {
return &o[index]
}
// Get returns the element at the specified position in this list.
func (c *CopyOnWriteList[T]) Get(index int) *T {
list := c.getList()
if index < 0 || index >= len(list) {
return nil
}
return get(list, index)
}
func (c *CopyOnWriteList[T]) set(index int, e T) (oldValue *T) {
lock := c.lock
lock.Lock()
defer lock.Unlock()
list := c.getList()
oldValue = get(list, index)
if reflect.DeepEqual(oldValue, e) {
c.setList(list)
} else {
newList := make([]T, len(list))
copy(newList, list)
newList[index] = e
c.setList(newList)
}
return
}
// Set replaces the element at the specified position in this list with the specified element.
func (c *CopyOnWriteList[T]) Set(index int, e T) (oldValue *T, ok bool) {
list := c.getList()
if index < 0 || index >= len(list) {
return oldValue, false
}
return c.set(index, e), true
}
// Add appends the specified element to the end of this list.
func (c *CopyOnWriteList[T]) Add(e T) bool {
lock := c.lock
lock.Lock()
defer lock.Unlock()
list := c.getList()
newList := make([]T, len(list)+1)
copy(newList, list)
newList[len(list)] = e
c.setList(newList)
return true
}
// AddAll appends all the elements in the specified collection to the end of this list
func (c *CopyOnWriteList[T]) AddAll(e []T) bool {
lock := c.lock
lock.Lock()
defer lock.Unlock()
list := c.getList()
newList := make([]T, len(list)+len(e))
copy(newList, list)
copy(newList[len(list):], e)
c.setList(newList)
return true
}
// AddByIndex inserts the specified element at the specified position in this list.
func (c *CopyOnWriteList[T]) AddByIndex(index int, e T) bool {
lock := c.lock
lock.Lock()
defer lock.Unlock()
list := c.getList()
length := len(list)
if index < 0 || index > length {
return false
}
var newList []T
var numMove = length - index
if numMove == 0 {
newList = make([]T, length+1)
copy(newList, list)
} else {
newList = make([]T, length+1)
copy(newList, list[:index])
copy(newList[index+1:], list[index:])
}
newList[index] = e
c.setList(newList)
return true
}
// delete removes the element at the specified position in this list.
func (c *CopyOnWriteList[T]) delete(index int) *T {
lock := c.lock
lock.Lock()
defer lock.Unlock()
list := c.getList()
length := len(list)
oldValue := get(list, index)
numMove := length - index - 1
var newList []T
if numMove == 0 {
newList = make([]T, length-1)
copy(newList, list[:index])
} else {
newList = make([]T, length-1)
copy(newList, list[:index])
copy(newList[index:], list[index+1:])
}
c.setList(newList)
return oldValue
}
// DeleteAt removes the element at the specified position in this list.
func (c *CopyOnWriteList[T]) DeleteAt(index int) (*T, bool) {
list := c.getList()
if index < 0 || index >= len(list) {
return nil, false
}
return c.delete(index), true
}
// DeleteBy removes the first occurrence of the specified element from this list, if it is present.
func (c *CopyOnWriteList[T]) DeleteBy(o T) (*T, bool) {
list := c.getList()
index := indexOf(o, list, 0, len(list))
if index == -1 {
return nil, false
}
return c.delete(index), true
}
// DeleteRange removes from this list all the elements whose index is between fromIndex, inclusive, and toIndex, exclusive.
// left close and right open
func (c *CopyOnWriteList[T]) DeleteRange(start int, end int) {
lock := c.lock
lock.Lock()
defer lock.Unlock()
list := c.getList()
length := len(list)
if start < 0 || end > length || start > end {
return
}
var newList []T
numMove := length - end
if numMove == 0 {
newList = make([]T, length-(end-start))
copy(newList, list[:start])
} else {
newList = make([]T, length-(end-start))
copy(newList, list[:start])
copy(newList[start:], list[end:])
}
c.setList(newList)
}
// DeleteIf removes all the elements of this collection that satisfy the given predicate.
func (c *CopyOnWriteList[T]) DeleteIf(f func(T) bool) {
lock := c.lock
lock.Lock()
defer lock.Unlock()
list := c.getList()
length := len(list)
var newList []T
for i := 0; i < length; i++ {
if !f(list[i]) {
newList = append(newList, list[i])
}
}
c.setList(newList)
}
// Equal returns true if the specified object is equal to this list.
func (c *CopyOnWriteList[T]) Equal(other *[]T) bool {
if other == nil {
return false
}
if c.Size() != len(*other) {
return false
}
list := c.getList()
otherList := NewCopyOnWriteList(*other).getList()
for i := 0; i < len(list); i++ {
if !reflect.DeepEqual(list[i], otherList[i]) {
return false
}
}
return true
}
// Clear removes all the elements from this list.
func (c *CopyOnWriteList[T]) Clear() {
lock := c.lock
lock.Lock()
defer lock.Unlock()
list := c.getList()
list = make([]T, 0)
c.setList(list)
}
// Merge a tow list to one, change the list
func (c *CopyOnWriteList[T]) Merge(other []T) {
lock := c.lock
lock.Lock()
defer lock.Unlock()
list := c.getList()
list = append(list, other...)
c.setList(list)
}
// ForEach performs the given action for each element of the Iterable until all elements have been processed
// or the action throws an exception.
func (c *CopyOnWriteList[T]) ForEach(f func(T)) {
list := c.getList()
for i := 0; i < len(list); i++ {
f(list[i])
}
}
// Sort sorts this list according to the order induced by the specified Comparator.
func (c *CopyOnWriteList[T]) Sort(compare func(o1 T, o2 T) bool) {
lock := c.lock
lock.Lock()
list := c.getList()
sort.Slice(list, func(i, j int) bool {
return compare(list[i], list[j])
})
c.setList(list)
}
func (c *CopyOnWriteList[T]) SubList(start int, end int) (newList []T) {
lock := c.lock
lock.Lock()
list := c.getList()
length := len(list)
defer lock.Unlock()
if start < 0 || end > length || start > end {
return []T{}
}
newList = make([]T, end-start)
copy(newList, list[start:end])
c.setList(newList)
return
}

View File

@@ -0,0 +1,235 @@
package datastructure
import (
"github.com/duke-git/lancet/v2/internal"
"testing"
)
func TestCopyOnWriteList_ValueOf(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_IndexOf")
of, ok := list.ValueOf(3)
assert.Equal(4, *of)
assert.Equal(true, ok)
_, ok = list.ValueOf(6)
assert.Equal(false, ok)
}
func TestCopyOnWriteList_Contain(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_Contains")
assert.Equal(true, list.Contain(3))
}
func TestCopyOnWriteList_IsEmpty(t *testing.T) {
list := NewCopyOnWriteList([]int{})
assert := internal.NewAssert(t, "CopyOnWriteList_IsEmpty")
assert.Equal(true, list.IsEmpty())
}
func TestCopyOnWriteList_Size(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_size")
assert.Equal(5, list.Size())
}
func TestCopyOnWriteList_GetList(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_GetList")
assert.Equal([]int{1, 2, 3, 4, 5}, list.getList())
}
func TestCopyOnWriteList_Get(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_Get")
i := list.Get(2)
assert.Equal(3, *i)
}
func TestCopyOnWriteList_Set(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_Set")
list.Set(2, 6)
assert.Equal(6, list.getList()[2])
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
list.Set(0, 6)
assert.Equal(6, list.getList()[0])
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
list.Set(0, 1)
assert.Equal(1, list.getList()[0])
}
func TestCopyOnWriteList_Add(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_Add")
list.Add(6)
assert.Equal([]int{1, 2, 3, 4, 5, 6}, list.getList())
}
func TestCopyOnWriteList_AddAll(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_AddAll")
list.AddAll([]int{6, 7, 8})
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8}, list.getList())
}
func TestCopyOnWriteList_AddByIndex(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_AddByIndex")
list.AddByIndex(2, 6)
assert.Equal([]int{1, 2, 6, 3, 4, 5}, list.getList())
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
list.AddByIndex(0, 6)
assert.Equal([]int{6, 1, 2, 3, 4, 5}, list.getList())
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
list.AddByIndex(5, 6)
assert.Equal([]int{1, 2, 3, 4, 5, 6}, list.getList())
}
func TestCopyOnWriteList_DeleteAt2(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_RemoveByIndex")
list.DeleteAt(2)
assert.Equal([]int{1, 2, 4, 5}, list.getList())
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
list.DeleteAt(4)
assert.Equal([]int{1, 2, 3, 4}, list.getList())
}
func TestCopyOnWriteList_RemoveByValue(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_RemoveByValue")
list.DeleteBy(3)
assert.Equal([]int{1, 2, 4, 5}, list.getList())
}
func TestCopyOnWriteList_DeleteRange(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
assert := internal.NewAssert(t, "CopyOnWriteList_RemoveRange")
list.DeleteRange(1, 3)
assert.Equal([]int{1, 4, 5}, list.getList())
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
list.DeleteRange(0, 5)
assert.Equal([]int{}, list.getList())
}
func TestCopyOnWriteList_LastIndexOf(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3})
assert := internal.NewAssert(t, "CopyOnWriteList_LastIndexOf")
assert.Equal(5, list.LastIndexOf(3))
}
func TestCopyOnWriteList_DeleteAt(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3})
assert := internal.NewAssert(t, "CopyOnWriteList_DeleteAt")
list.DeleteAt(2)
assert.Equal([]int{1, 2, 4, 5, 3}, list.getList())
}
func TestCopyOnWriteList_DeleteBy(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3})
assert := internal.NewAssert(t, "CopyOnWriteList_DeleteBy")
list.DeleteBy(3)
assert.Equal([]int{1, 2, 4, 5, 3}, list.getList())
}
func TestCopyOnWriteList_DeleteIf(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6})
assert := internal.NewAssert(t, "CopyOnWriteList_DeleteIf")
list.DeleteIf(func(i int) bool {
return i%2 == 0
})
assert.Equal([]int{1, 3, 5, 3}, list.getList())
}
func TestCopyOnWriteList_Equal(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6})
assert := internal.NewAssert(t, "CopyOnWriteList_Equal")
assert.Equal(true, list.Equal(&[]int{1, 2, 3, 4, 5, 3, 6}))
}
func TestCopyOnWriteList_ForEach(t *testing.T) {
testList := make([]int, 0)
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6})
assert := internal.NewAssert(t, "CopyOnWriteList_ForEach")
list.ForEach(func(i int) {
testList = append(testList, i)
})
assert.Equal([]int{1, 2, 3, 4, 5, 3, 6}, testList)
list.ForEach(func(i int) {
list.Add(i)
})
assert.Equal([]int{1, 2, 3, 4, 5, 3, 6, 1, 2, 3, 4, 5, 3, 6}, list.getList())
}
func TestCopyOnWriteList_Clear(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6})
assert := internal.NewAssert(t, "CopyOnWriteList_Clear")
list.Clear()
assert.Equal([]int{}, list.getList())
}
func TestCopyOnWriteList_Merge(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9})
assert := internal.NewAssert(t, "CopyOnWriteList_Merge")
list.Merge([]int{2, 4, 6, 8, 10})
assert.Equal([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}, list.getList())
}
func TestCopyOnWriteList_Sort(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
assert := internal.NewAssert(t, "CopyOnWriteList_Sort")
list.Sort(func(i, j int) bool {
return i < j
})
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, list.getList())
}
func TestCopyOnWriteList_IndexOf(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
assert := internal.NewAssert(t, "CopyOnWriteList_IndexOf")
assert.Equal(0, list.IndexOf(1))
assert.Equal(9, list.IndexOf(10))
assert.Equal(-1, list.IndexOf(11))
}
func TestCopyOnWriteList_SubList(t *testing.T) {
list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
assert := internal.NewAssert(t, "CopyOnWriteList_SubList")
list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
subList := list.SubList(1, 3)
assert.Equal([]int{3, 5}, subList)
list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
subList = list.SubList(1, 1)
assert.Equal([]int{}, subList)
list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
assert.Equal(10, list.Size())
subList = list.SubList(1, 10)
assert.Equal([]int{3, 5, 7, 9, 2, 4, 6, 8, 10}, subList)
list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
subList = list.SubList(11, 1)
assert.Equal([]int{}, subList)
}

418
datastructure/list/list.go Normal file
View File

@@ -0,0 +1,418 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure. list is a linear table, implemented with slice.
package datastructure
import (
"reflect"
"github.com/duke-git/lancet/v2/iterator"
)
// List is a linear table, implemented with slice.
type List[T any] struct {
data []T
}
// NewList return a pointer of List.
func NewList[T any](data []T) *List[T] {
return &List[T]{data: data}
}
// Data return list data.
func (l *List[T]) Data() []T {
return l.data
}
// ValueOf return the value pointer at index of list data.
func (l *List[T]) ValueOf(index int) (*T, bool) {
if index < 0 || index >= len(l.data) {
return nil, false
}
return &l.data[index], true
}
// IndexOf returns the index of value. if not found return -1.
func (l *List[T]) IndexOf(value T) int {
index := -1
data := l.data
for i, v := range data {
if reflect.DeepEqual(v, value) {
index = i
break
}
}
return index
}
// LastIndexOf returns the index of the last occurrence of the value in this list.
// if not found return -1.
func (l *List[T]) LastIndexOf(value T) int {
index := -1
data := l.data
for i := len(data) - 1; i >= 0; i-- {
if reflect.DeepEqual(data[i], value) {
index = i
break
}
}
return index
}
// IndexOfFunc returns the first index satisfying f(v)
// if not found return -1.
func (l *List[T]) IndexOfFunc(f func(T) bool) int {
index := -1
data := l.data
for i, v := range data {
if f(v) {
index = i
break
}
}
return index
}
// LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying f(data[i])
// if not found return -1.
func (l *List[T]) LastIndexOfFunc(f func(T) bool) int {
index := -1
data := l.data
for i := len(data) - 1; i >= 0; i-- {
if f(data[i]) {
index = i
break
}
}
return index
}
// Contain checks if the value in the list or not.
func (l *List[T]) Contain(value T) bool {
data := l.data
for _, v := range data {
if reflect.DeepEqual(v, value) {
return true
}
}
return false
}
// Push append value to the list data.
func (l *List[T]) Push(value T) {
l.data = append(l.data, value)
}
// InsertAtFirst insert value into list at first index.
func (l *List[T]) InsertAtFirst(value T) {
l.InsertAt(0, value)
}
// InsertAtLast insert value into list at last index.
func (l *List[T]) InsertAtLast(value T) {
l.InsertAt(len(l.data), value)
}
// InsertAt insert value into list at index.
func (l *List[T]) InsertAt(index int, value T) {
data := l.data
size := len(data)
if index < 0 || index > size {
return
}
l.data = append(data[:index], append([]T{value}, data[index:]...)...)
}
// PopFirst delete the first value of list and return it.
func (l *List[T]) PopFirst() (*T, bool) {
if len(l.data) == 0 {
return nil, false
}
v := l.data[0]
l.DeleteAt(0)
return &v, true
}
// PopLast delete the last value of list and return it.
func (l *List[T]) PopLast() (*T, bool) {
size := len(l.data)
if size == 0 {
return nil, false
}
v := l.data[size-1]
l.DeleteAt(size - 1)
return &v, true
}
// DeleteAt delete the value of list at index.
func (l *List[T]) DeleteAt(index int) {
data := l.data
size := len(data)
if index < 0 || index > size-1 {
return
}
if index == size-1 {
data = data[:index]
} else {
data = append(data[:index], data[index+1:]...)
}
l.data = data
}
// DeleteIf delete all satisfying f(data[i]), returns count of removed elements
func (l *List[T]) DeleteIf(f func(T) bool) int {
data := l.data
size := len(data)
var c int
for index := 0; index < len(data); index++ {
if !f(data[index]) {
continue
}
if index == size-1 {
data = data[:index]
} else {
data = append(data[:index], data[index+1:]...)
index--
}
c++
}
if c > 0 {
l.data = data
}
return c
}
// UpdateAt update value of list at index, index shoud between 0 and list size -1
func (l *List[T]) UpdateAt(index int, value T) {
data := l.data
size := len(data)
if index < 0 || index >= size {
return
}
l.data = append(data[:index], append([]T{value}, data[index+1:]...)...)
}
// Equal compare list to other list, use reflect.DeepEqual.
func (l *List[T]) Equal(other *List[T]) bool {
if len(l.data) != len(other.data) {
return false
}
for i := 0; i < len(l.data); i++ {
if !reflect.DeepEqual(l.data[i], other.data[i]) {
return false
}
}
return true
}
// IsEmpty check if the list is empty or not.
func (l *List[T]) IsEmpty() bool {
return len(l.data) == 0
}
// Clear the data of list.
func (l *List[T]) Clear() {
l.data = make([]T, 0)
}
// Clone return a copy of list.
func (l *List[T]) Clone() *List[T] {
cl := NewList(make([]T, len(l.data)))
copy(cl.data, l.data)
return cl
}
// Merge two list, return new list, don't change original list.
func (l *List[T]) Merge(other *List[T]) *List[T] {
l1, l2 := len(l.data), len(other.data)
ml := NewList(make([]T, l1+l2))
data := append([]T{}, append(l.data, other.data...)...)
ml.data = data
return ml
}
// Size return number of list data items.
func (l *List[T]) Size() int {
return len(l.data)
}
// Cap return cap of the inner data.
func (l *List[T]) Cap() int {
return cap(l.data)
}
// Swap the value of index i and j in list.
func (l *List[T]) Swap(i, j int) {
size := len(l.data)
if i < 0 || i >= size || j < 0 || j >= size {
return
}
l.data[i], l.data[j] = l.data[j], l.data[i]
}
// Reverse the item order of list.
func (l *List[T]) Reverse() {
for i, j := 0, len(l.data)-1; i < j; i, j = i+1, j-1 {
l.data[i], l.data[j] = l.data[j], l.data[i]
}
}
// Unique delete duplicate items in list.
func (l *List[T]) Unique() {
data := l.data
size := len(data)
uniqueData := make([]T, 0)
for i := 0; i < size; i++ {
value := data[i]
skip := true
for _, v := range uniqueData {
if reflect.DeepEqual(value, v) {
skip = false
break
}
}
if skip {
uniqueData = append(uniqueData, value)
}
}
l.data = uniqueData
}
// Union creates a new list contain all element in list l and other, delete duplicate element.
func (l *List[T]) Union(other *List[T]) *List[T] {
result := NewList([]T{})
result.data = append(result.data, l.data...)
result.data = append(result.data, other.data...)
result.Unique()
return result
}
// Intersection creates a new list whose element both be contained in list l and other.
func (l *List[T]) Intersection(other *List[T]) *List[T] {
result := NewList(make([]T, 0))
for _, v := range l.data {
if other.Contain(v) {
result.data = append(result.data, v)
}
}
return result
}
// Difference returns the difference between two collections.
// return a list whose element in the original list, not in the given list.
func (l *List[T]) Difference(other *List[T]) *List[T] {
result := NewList(make([]T, 0))
intersectList := l.Intersection(other)
for _, v := range l.data {
if !intersectList.Contain(v) {
result.data = append(result.data, v)
}
}
return result
}
// SymmetricDifference oppoiste operation of intersection function.
func (l *List[T]) SymmetricDifference(other *List[T]) *List[T] {
result := NewList(make([]T, 0))
intersectList := l.Intersection(other)
for _, v := range l.data {
if !intersectList.Contain(v) {
result.data = append(result.data, v)
}
}
for _, v := range other.data {
if !intersectList.Contain(v) {
result.data = append(result.data, v)
}
}
return result
}
// SubList returns a sub list of the original list between the specified fromIndex, inclusive, and toIndex, exclusive.
func (l *List[T]) SubList(fromIndex, toIndex int) *List[T] {
data := l.data[fromIndex:toIndex]
subList := make([]T, len(data))
copy(subList, data)
return NewList(subList)
}
// ForEach performs the given action for each element of the list.
func (l *List[T]) ForEach(consumer func(T)) {
for _, it := range l.data {
consumer(it)
}
}
// RetainAll retains only the elements in this list that are contained in the given list.
func (l *List[T]) RetainAll(list *List[T]) bool {
return l.batchRemove(list, true)
}
// DeleteAll removes from this list all of its elements that are contained in the given list.
func (l *List[T]) DeleteAll(list *List[T]) bool {
return l.batchRemove(list, false)
}
func (l *List[T]) batchRemove(list *List[T], complement bool) bool {
var (
w = 0
data = l.data
size = len(data)
)
for i := 0; i < size; i++ {
if list.Contain(data[i]) == complement {
data[w] = data[i]
w++
}
}
if w != size {
l.data = data[:w]
return true
}
return false
}
// Iterator returns an iterator over the elements in this list in proper sequence.
func (l *List[T]) Iterator() iterator.Iterator[T] {
return iterator.FromSlice(l.data)
}
// ListToMap convert a list to a map based on iteratee function.
func ListToMap[T any, K comparable, V any](list *List[T], iteratee func(T) (K, V)) map[K]V {
result := make(map[K]V, list.Size())
for _, item := range list.data {
k, v := iteratee(item)
result[k] = v
}
return result
}

View File

@@ -0,0 +1,531 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestListData(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestListData")
list := NewList([]int{1, 2, 3})
assert.Equal([]int{1, 2, 3}, list.Data())
}
func TestValueOf(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestValueOf")
list := NewList([]int{1, 2, 3})
v, ok := list.ValueOf(0)
assert.Equal(1, *v)
assert.Equal(true, ok)
_, ok = list.ValueOf(3)
assert.Equal(false, ok)
}
func TestIndexOf(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIndexOf")
list := NewList([]int{1, 2, 3})
i := list.IndexOf(1)
assert.Equal(0, i)
i = list.IndexOf(4)
assert.Equal(-1, i)
}
func TestIndexOfFunc(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIndexOf")
list := NewList([]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 TestLastIndexOf(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIndexOf")
list := NewList([]int{1, 2, 3, 3, 3, 3, 4, 5, 6, 9})
i := list.LastIndexOf(3)
assert.Equal(5, i)
i = list.LastIndexOf(10)
assert.Equal(-1, i)
i = list.LastIndexOf(4)
assert.Equal(6, i)
i = list.LastIndexOf(1)
assert.Equal(0, i)
}
func TestLastIndexOfFunc(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIndexOf")
list := NewList([]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)
}
func TestContain(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestContain")
list := NewList([]int{1, 2, 3})
assert.Equal(true, list.Contain(1))
assert.Equal(false, list.Contain(0))
}
func TestPush(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPush")
list := NewList([]int{1, 2, 3})
list.Push(4)
assert.Equal([]int{1, 2, 3, 4}, list.Data())
}
func TestInsertAtFirst(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestInsertAtFirst")
list := NewList([]int{1, 2, 3})
list.InsertAtFirst(0)
assert.Equal([]int{0, 1, 2, 3}, list.Data())
}
func TestInsertAtLast(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestInsertAtLast")
list := NewList([]int{1, 2, 3})
list.InsertAtLast(4)
assert.Equal([]int{1, 2, 3, 4}, list.Data())
}
func TestInsertAt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestInsertAt")
list := NewList([]int{1, 2, 3})
list.InsertAt(-1, 0)
assert.Equal([]int{1, 2, 3}, list.Data())
list.InsertAt(4, 0)
assert.Equal([]int{1, 2, 3}, list.Data())
list.InsertAt(0, 0)
assert.Equal([]int{0, 1, 2, 3}, list.Data())
list.InsertAt(4, 4)
assert.Equal([]int{0, 1, 2, 3, 4}, list.Data())
}
func TestPopFirst(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPopFirst")
list := NewList([]int{1, 2, 3})
v, ok := list.PopFirst()
assert.Equal(1, *v)
assert.Equal(true, ok)
assert.Equal([]int{2, 3}, list.Data())
list2 := NewList([]int{})
_, ok = list2.PopFirst()
assert.Equal(false, ok)
assert.Equal([]int{}, list2.Data())
}
func TestPopLast(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPopLast")
list := NewList([]int{1, 2, 3})
v, ok := list.PopLast()
assert.Equal(3, *v)
assert.Equal(true, ok)
assert.Equal([]int{1, 2}, list.Data())
list2 := NewList([]int{})
_, ok = list2.PopLast()
assert.Equal(false, ok)
assert.Equal([]int{}, list2.Data())
}
func TestDeleteAt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDeleteAt")
list := NewList([]int{1, 2, 3, 4})
list.DeleteAt(-1)
assert.Equal([]int{1, 2, 3, 4}, list.Data())
list.DeleteAt(4)
assert.Equal([]int{1, 2, 3, 4}, list.Data())
list.DeleteAt(0)
assert.Equal([]int{2, 3, 4}, list.Data())
list.DeleteAt(2)
assert.Equal([]int{2, 3}, list.Data())
}
func TestUpdateAt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUpdateAt")
list := NewList([]int{1, 2, 3, 4})
list.UpdateAt(-1, 0)
assert.Equal([]int{1, 2, 3, 4}, list.Data())
list.UpdateAt(4, 0)
assert.Equal([]int{1, 2, 3, 4}, list.Data())
list.UpdateAt(0, 5)
assert.Equal([]int{5, 2, 3, 4}, list.Data())
list.UpdateAt(3, 1)
assert.Equal([]int{5, 2, 3, 1}, list.Data())
}
func TestEqual(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEqual")
list1 := NewList([]int{1, 2, 3, 4})
list2 := NewList([]int{1, 2, 3, 4})
list3 := NewList([]int{1, 2, 3})
assert.Equal(true, list1.Equal(list2))
assert.Equal(false, list1.Equal(list3))
}
func TestIsEmpty(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsEmpty")
list1 := NewList([]int{1, 2, 3, 4})
list2 := NewList([]int{})
assert.Equal(false, list1.IsEmpty())
assert.Equal(true, list2.IsEmpty())
}
func TestIsClear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsClear")
list1 := NewList([]int{1, 2, 3, 4})
list1.Clear()
empty := NewList([]int{})
assert.Equal(empty, list1)
}
func TestClone(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestClone")
list1 := NewList([]int{1, 2, 3, 4})
list2 := list1.Clone()
assert.Equal(true, list1.Equal(list2))
}
func TestMerge(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMerge")
list1 := NewList([]int{1, 2, 3, 4})
list2 := NewList([]int{4, 5, 6})
expected := NewList([]int{1, 2, 3, 4, 4, 5, 6})
list3 := list1.Merge(list2)
assert.Equal(true, expected.Equal(list3))
}
func TestSize(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSize")
list := NewList([]int{1, 2, 3, 4})
empty := NewList([]int{})
assert.Equal(4, list.Size())
assert.Equal(0, empty.Size())
}
func TestCap(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCap")
data := make([]int, 0, 100)
list := NewList(data)
assert.Equal(100, list.Cap())
data = make([]int, 0)
list = NewList(data)
assert.Equal(0, list.Cap())
}
func TestSwap(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSwap")
list := NewList([]int{1, 2, 3, 4})
expected := NewList([]int{4, 2, 3, 1})
list.Swap(0, 3)
assert.Equal(true, expected.Equal(list))
}
func TestReverse(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestReverse")
list := NewList([]int{1, 2, 3, 4})
expected := NewList([]int{4, 3, 2, 1})
list.Reverse()
assert.Equal(true, expected.Equal(list))
}
func TestUnique(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUnique")
list := NewList([]int{1, 2, 2, 3, 4})
expected := NewList([]int{1, 2, 3, 4})
list.Unique()
assert.Equal(true, expected.Equal(list))
}
func TestUnion(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUnion")
list1 := NewList([]int{1, 2, 3, 4})
list2 := NewList([]int{4, 5, 6})
expected := NewList([]int{1, 2, 3, 4, 5, 6})
list3 := list1.Union(list2)
assert.Equal(true, expected.Equal(list3))
}
func TestIntersection(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIntersection")
list1 := NewList([]int{1, 2, 3, 4})
list2 := NewList([]int{4, 5, 6})
expected := NewList([]int{4})
list3 := list1.Intersection(list2)
assert.Equal(true, expected.Equal(list3))
}
func TestDifference(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDifference")
list1 := NewList([]int{1, 2, 3})
list2 := NewList([]int{1, 2, 4})
expected := NewList([]int{3})
list3 := list1.Difference(list2)
assert.Equal(true, expected.Equal(list3))
}
func TestSymmetricDifference(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSymmetricDifference")
list1 := NewList([]int{1, 2, 3})
list2 := NewList([]int{1, 2, 4})
expected := NewList([]int{3, 4})
list3 := list1.SymmetricDifference(list2)
assert.Equal(true, expected.Equal(list3))
}
func TestSubSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSubSlice")
list := NewList([]int{1, 2, 3, 4, 5, 8})
subList := list.SubList(2, 5)
assert.Equal([]int{3, 4, 5}, subList.Data())
}
func BenchmarkSubSlice(b *testing.B) {
list := NewList([]int{1, 2, 3, 4, 5, 8})
for n := 0; n < b.N; n++ {
list.SubList(2, 5)
}
}
func TestDeleteIf(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDeleteIf")
list := NewList([]int{1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1})
count := list.DeleteIf(func(a int) bool { return a == 1 })
assert.Equal([]int{2, 3, 4}, list.Data())
assert.Equal(12, count)
count = list.DeleteIf(func(a int) bool { return a == 5 })
assert.Equal([]int{2, 3, 4}, list.Data())
assert.Equal(0, count)
}
func TestForEach(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestForEach")
list := NewList([]int{1, 2, 3, 4})
rs := make([]int, 0)
list.ForEach(func(i int) {
rs = append(rs, i)
})
assert.Equal([]int{1, 2, 3, 4}, rs)
}
func TestRetainAll(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRetainAll")
list := NewList([]int{1, 2, 3, 4})
list1 := NewList([]int{1, 2, 3, 4})
list2 := NewList([]int{1, 2, 3, 4})
retain := NewList([]int{1, 2})
retain1 := NewList([]int{2, 3})
retain2 := NewList([]int{1, 2, 5})
list.RetainAll(retain)
list1.RetainAll(retain1)
list2.RetainAll(retain2)
assert.Equal([]int{1, 2}, list.Data())
assert.Equal([]int{2, 3}, list1.Data())
assert.Equal([]int{1, 2}, list2.Data())
}
func TestDeleteAll(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDeleteAll")
list := NewList([]int{1, 2, 3, 4})
list1 := NewList([]int{1, 2, 3, 4})
list2 := NewList([]int{1, 2, 3, 4})
del := NewList([]int{1})
del1 := NewList([]int{2, 3})
del2 := NewList([]int{1, 2, 5})
list.DeleteAll(del)
list1.DeleteAll(del1)
list2.DeleteAll(del2)
assert.Equal([]int{2, 3, 4}, list.Data())
assert.Equal([]int{1, 4}, list1.Data())
assert.Equal([]int{3, 4}, list2.Data())
}
func TestIterator(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIterator")
list := NewList([]int{1, 2, 3, 4})
iterator := list.Iterator()
rs := make([]int, 0)
for iterator.HasNext() {
item, _ := iterator.Next()
rs = append(rs, item)
}
assert.Equal([]int{1, 2, 3, 4}, rs)
}
func TestListToMap(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "ListToMap")
list := NewList([]int{1, 2, 3, 4})
result := ListToMap(list, func(n int) (int, bool) {
return n, n > 1
})
expected := map[int]bool{1: false, 2: true, 3: true, 4: true}
assert.Equal(expected, result)
}

51
datastructure/node.go Normal file
View File

@@ -0,0 +1,51 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure implements some data structure.
package datastructure
// LinkNode is a linkedlist node, which have a Value and Pre points to previous node, Next points to a next node of the link.
type LinkNode[T any] struct {
Value T
Pre *LinkNode[T]
Next *LinkNode[T]
}
// NewLinkNode return a LinkNode pointer
func NewLinkNode[T any](value T) *LinkNode[T] {
return &LinkNode[T]{value, nil, nil}
}
// StackNode is a node in stack, which have a Value and Next pointer points to next node in the stack.
type StackNode[T any] struct {
Value T
Next *StackNode[T]
}
// NewStackNode return a StackNode pointer
func NewStackNode[T any](value T) *StackNode[T] {
return &StackNode[T]{value, nil}
}
// QueueNode is a node in a queue, which have a Value and Next pointer points to next node in the queue.
type QueueNode[T any] struct {
Value T
Next *QueueNode[T]
}
// NewQueueNode return a QueueNode pointer
func NewQueueNode[T any](value T) *QueueNode[T] {
return &QueueNode[T]{value, nil}
}
// TreeNode is node of tree
type TreeNode[T any] struct {
Value T
Left *TreeNode[T]
Right *TreeNode[T]
}
// NewTreeNode return a TreeNode pointer
func NewTreeNode[T any](val T) *TreeNode[T] {
return &TreeNode[T]{val, nil, nil}
}

View File

@@ -0,0 +1,124 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure.
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
package datastructure
import (
"fmt"
"reflect"
)
// ArrayQueue implements queue with slice
type ArrayQueue[T any] struct {
items []T
head int
tail int
capacity int
size int
}
func NewArrayQueue[T any](capacity int) *ArrayQueue[T] {
return &ArrayQueue[T]{
items: make([]T, 0, capacity),
head: 0,
tail: 0,
capacity: capacity,
size: 0,
}
}
// Data return slice of queue data
func (q *ArrayQueue[T]) Data() []T {
items := []T{}
for i := q.head; i < q.tail; i++ {
items = append(items, q.items[i])
}
return items
}
// Size return number of elements in queue
func (q *ArrayQueue[T]) Size() int {
return q.size
}
// IsEmpty checks if queue is empty or not
func (q *ArrayQueue[T]) IsEmpty() bool {
return q.size == 0
}
// IsFull checks if queue is full or not
func (q *ArrayQueue[T]) IsFull() bool {
return q.size == q.capacity
}
// Front return front value of queue
func (q *ArrayQueue[T]) Front() T {
return q.items[0]
}
// Back return back value of queue
func (q *ArrayQueue[T]) Back() T {
return q.items[q.size-1]
}
// EnQueue put element into queue
func (q *ArrayQueue[T]) Enqueue(item T) bool {
if q.head == 0 && q.tail == q.capacity {
return false
} else if q.head != 0 && q.tail == q.capacity {
for i := q.head; i < q.tail; i++ {
q.items[i-q.head] = q.items[i]
}
q.tail = q.tail - q.head
q.head = 0
}
q.items = append(q.items, item)
q.tail++
q.size++
return true
}
// DeQueue remove head element of queue and return it, if queue is empty, return nil and error
func (q *ArrayQueue[T]) Dequeue() (T, bool) {
var item T
if q.head == q.tail {
return item, false
}
item = q.items[q.head]
q.head++
q.size--
return item, true
}
// Clear the queue data
func (q *ArrayQueue[T]) Clear() {
capacity := q.capacity
q.items = make([]T, 0, capacity)
q.head = 0
q.tail = 0
q.size = 0
q.capacity = capacity
}
// Contain checks if the value is in queue or not
func (q *ArrayQueue[T]) Contain(value T) bool {
for _, v := range q.items {
if reflect.DeepEqual(v, value) {
return true
}
}
return false
}
// Print queue data
func (q *ArrayQueue[T]) Print() {
info := "["
for i := q.head; i < q.tail; i++ {
info += fmt.Sprintf("%+v, ", q.items[i])
}
info += "]"
fmt.Println(info)
}

View File

@@ -0,0 +1,119 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestArrayQueue_Enqueue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayQueue_Enqueue")
queue := NewArrayQueue[int](5)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
data := queue.Data()
size := queue.Size()
assert.Equal([]int{1, 2, 3}, data)
assert.Equal(3, size)
}
func TestArrayQueue_Dequeue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayQueue_Dequeue")
queue := NewArrayQueue[int](4)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
val, ok := queue.Dequeue()
assert.Equal(true, ok)
assert.Equal(1, val)
assert.Equal([]int{2, 3}, queue.Data())
}
func TestArrayQueue_Front(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayQueue_Front")
queue := NewArrayQueue[int](4)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
val := queue.Front()
assert.Equal(1, val)
assert.Equal([]int{1, 2, 3}, queue.Data())
}
func TestArrayQueue_Back(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayQueue_Back")
queue := NewArrayQueue[int](4)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
val := queue.Back()
assert.Equal(3, val)
assert.Equal([]int{1, 2, 3}, queue.Data())
}
func TestArrayQueue_Contain(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayQueue_Contain")
queue := NewArrayQueue[int](4)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
assert.Equal(true, queue.Contain(1))
assert.Equal(false, queue.Contain(4))
}
func TestArrayQueue_Clear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayQueue_Clear")
queue := NewArrayQueue[int](4)
assert.Equal(true, queue.IsEmpty())
assert.Equal(0, queue.Size())
queue.Enqueue(1)
assert.Equal(false, queue.IsEmpty())
assert.Equal(1, queue.Size())
queue.Clear()
assert.Equal(true, queue.IsEmpty())
assert.Equal(0, queue.Size())
}
func TestArrayQueue_IsFull(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayQueue_IsFull")
queue := NewArrayQueue[int](3)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
assert.Equal(true, queue.IsFull())
}

View File

@@ -0,0 +1,123 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure.
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
package datastructure
import (
"errors"
"fmt"
"reflect"
)
// CircularQueue implements circular queue with slice,
// last index of CircularQueue don't contain value, so acturl capacity is capacity - 1
type CircularQueue[T any] struct {
data []T
front int
rear int
capacity int
}
// NewCircularQueue return a empty CircularQueue pointer
func NewCircularQueue[T any](capacity int) *CircularQueue[T] {
data := make([]T, capacity)
return &CircularQueue[T]{data: data, front: 0, rear: 0, capacity: capacity}
}
// Data return slice of queue data
func (q *CircularQueue[T]) Data() []T {
data := []T{}
front := q.front
rear := q.rear
if front <= rear {
return q.data[front:rear]
}
data = append(data, q.data[front:]...)
data = append(data, q.data[0:rear]...)
return data
}
// Size return number of elements in circular queue
func (q *CircularQueue[T]) Size() int {
if q.capacity == 0 {
return 0
}
return (q.rear - q.front + q.capacity) % q.capacity
}
// IsEmpty checks if queue is empty or not
func (q *CircularQueue[T]) IsEmpty() bool {
return q.front == q.rear
}
// IsFull checks if queue is full or not
func (q *CircularQueue[T]) IsFull() bool {
return (q.rear+1)%q.capacity == q.front
}
// Front return front value of queue
func (q *CircularQueue[T]) Front() T {
return q.data[q.front]
}
// Back return back value of queue
func (q *CircularQueue[T]) Back() T {
if q.rear-1 >= 0 {
return q.data[q.rear-1]
}
return q.data[q.capacity-1]
}
// Enqueue put element into queue
func (q *CircularQueue[T]) Enqueue(value T) error {
if q.IsFull() {
return errors.New("queue is full!")
}
q.data[q.rear] = value
q.rear = (q.rear + 1) % q.capacity
return nil
}
// Dequeue remove head element of queue and return it, if queue is empty, return nil and error
func (q *CircularQueue[T]) Dequeue() (*T, error) {
if q.IsEmpty() {
return nil, errors.New("queue is empty")
}
headItem := q.data[q.front]
var t T
q.data[q.front] = t
q.front = (q.front + 1) % q.capacity
return &headItem, nil
}
// Clear the queue data
func (q *CircularQueue[T]) Clear() {
q.data = []T{}
q.front = 0
q.rear = 0
q.capacity = 0
}
// Contain checks if the value is in queue or not
func (q *CircularQueue[T]) Contain(value T) bool {
for _, v := range q.data {
if reflect.DeepEqual(v, value) {
return true
}
}
return false
}
// Print queue data
func (q *CircularQueue[T]) Print() {
fmt.Printf("%+v\n", q)
}

View File

@@ -0,0 +1,159 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestCircularQueue_Enqueue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCircularQueue_Enqueue")
queue := NewCircularQueue[int](6)
err := queue.Enqueue(1)
assert.IsNil(err)
err = queue.Enqueue(2)
assert.IsNil(err)
err = queue.Enqueue(3)
assert.IsNil(err)
err = queue.Enqueue(4)
assert.IsNil(err)
err = queue.Enqueue(5)
assert.IsNil(err)
assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data())
assert.Equal(5, queue.Size())
err = queue.Enqueue(6)
assert.IsNotNil(err)
}
func TestCircularQueue_Dequeue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCircularQueue_DeQueue")
queue := NewCircularQueue[int](4)
assert.Equal(true, queue.IsEmpty())
err := queue.Enqueue(1)
assert.IsNil(err)
err = queue.Enqueue(2)
assert.IsNil(err)
err = queue.Enqueue(3)
assert.IsNil(err)
val, err := queue.Dequeue()
assert.IsNil(err)
assert.Equal(1, *val)
assert.Equal(false, queue.IsFull())
val, _ = queue.Dequeue()
assert.Equal(2, *val)
assert.Equal(false, queue.IsFull())
}
func TestCircularQueue_Front(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCircularQueue_Front")
queue := NewCircularQueue[int](6)
err := queue.Enqueue(1)
assert.IsNil(err)
err = queue.Enqueue(2)
assert.IsNil(err)
err = queue.Enqueue(3)
assert.IsNil(err)
val := queue.Front()
assert.IsNil(err)
assert.Equal(1, val)
assert.Equal(3, queue.Size())
}
func TestCircularQueue_Back(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCircularQueue_Back")
queue := NewCircularQueue[int](3)
assert.Equal(true, queue.IsEmpty())
err := queue.Enqueue(1)
assert.IsNil(err)
err = queue.Enqueue(2)
assert.IsNil(err)
assert.Equal(2, queue.Back())
val, _ := queue.Dequeue()
assert.Equal(1, *val)
err = queue.Enqueue(3)
assert.IsNil(err)
assert.Equal(3, queue.Back())
}
func TestCircularQueue_Contain(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCircularQueue_Contain")
queue := NewCircularQueue[int](2)
err := queue.Enqueue(1)
assert.IsNil(err)
assert.Equal(true, queue.Contain(1))
assert.Equal(false, queue.Contain(2))
}
func TestCircularQueue_Clear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCircularQueue_Clear")
queue := NewCircularQueue[int](3)
assert.Equal(true, queue.IsEmpty())
assert.Equal(0, queue.Size())
err := queue.Enqueue(1)
assert.IsNil(err)
assert.Equal(false, queue.IsEmpty())
assert.Equal(1, queue.Size())
queue.Clear()
assert.Equal(true, queue.IsEmpty())
assert.Equal(0, queue.Size())
}
func TestCircularQueue_Data(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCircularQueue_Data")
queue := NewCircularQueue[int](3)
err := queue.Enqueue(1)
assert.IsNil(err)
err = queue.Enqueue(2)
assert.IsNil(err)
assert.Equal([]int{1, 2}, queue.Data())
}

View File

@@ -0,0 +1,122 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure.
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
package datastructure
import (
"errors"
"fmt"
"reflect"
"github.com/duke-git/lancet/v2/datastructure"
)
// LinkedQueue implements queue with link list
type LinkedQueue[T any] struct {
head *datastructure.QueueNode[T]
tail *datastructure.QueueNode[T]
length int
}
// NewLinkedQueue return a empty LinkedQueue pointer
func NewLinkedQueue[T any]() *LinkedQueue[T] {
return &LinkedQueue[T]{head: nil, tail: nil, length: 0}
}
// Data return slice of queue data
func (q *LinkedQueue[T]) Data() []T {
res := []T{}
current := q.head
for current != nil {
res = append(res, current.Value)
current = current.Next
}
return res
}
// Size return length of queue data
func (q *LinkedQueue[T]) Size() int {
return q.length
}
// IsEmpty checks if queue is empty or not
func (q *LinkedQueue[T]) IsEmpty() bool {
return q.length == 0
}
// Enqueue put element into queue
func (q *LinkedQueue[T]) Enqueue(value T) {
newNode := datastructure.NewQueueNode(value)
if q.IsEmpty() {
q.head = newNode
q.tail = newNode
} else {
q.tail.Next = newNode
q.tail = newNode
}
q.length++
}
// Dequeue delete head element of queue then return it, if queue is empty, return nil and error
func (q *LinkedQueue[T]) Dequeue() (*T, error) {
if q.IsEmpty() {
return nil, errors.New("queue is empty")
}
head := q.head
q.head = q.head.Next
q.length--
return &head.Value, nil
}
// Front return front value of queue
func (q *LinkedQueue[T]) Front() (*T, error) {
if q.IsEmpty() {
return nil, errors.New("queue is empty")
}
return &q.head.Value, nil
}
// Back return back value of queue
func (q *LinkedQueue[T]) Back() (*T, error) {
if q.IsEmpty() {
return nil, errors.New("queue is empty")
}
return &q.tail.Value, nil
}
// Clear clear the queue data
func (q *LinkedQueue[T]) Clear() {
q.head = nil
q.tail = nil
q.length = 0
}
// Print all nodes info of queue link
func (q *LinkedQueue[T]) Print() {
current := q.head
info := "[ "
for current != nil {
info += fmt.Sprintf("%+v, ", current)
current = current.Next
}
info += " ]"
fmt.Println(info)
}
// Contain checks if the value is in queue or not
func (q *LinkedQueue[T]) Contain(value T) bool {
current := q.head
for current != nil {
if reflect.DeepEqual(current.Value, value) {
return true
}
current = current.Next
}
return false
}

View File

@@ -0,0 +1,106 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestLinkedQueue_Enqueue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLinkedQueue_Enqueue")
queue := NewLinkedQueue[int]()
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
assert.Equal([]int{1, 2, 3}, queue.Data())
assert.Equal(3, queue.Size())
}
func TestLinkedQueue_Dequeue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLinkedQueue_DeQueue")
queue := NewLinkedQueue[int]()
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
val, _ := queue.Dequeue()
queue.Print()
assert.Equal([]int{2, 3}, queue.Data())
assert.Equal(1, *val)
}
func TestLinkedQueue_Front(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLinkedQueue_Front")
queue := NewLinkedQueue[int]()
_, err := queue.Front()
assert.IsNotNil(err)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
val, err := queue.Front()
assert.Equal(1, *val)
assert.IsNil(err)
}
func TestLinkedQueue_Back(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLinkedQueue_Back")
queue := NewLinkedQueue[int]()
_, err := queue.Back()
assert.IsNotNil(err)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
val, err := queue.Back()
assert.Equal(3, *val)
assert.IsNil(err)
}
func TestLinkedQueue_Clear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLinkedQueue_Back")
queue := NewLinkedQueue[int]()
assert.Equal(true, queue.IsEmpty())
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
assert.Equal(false, queue.IsEmpty())
queue.Clear()
assert.Equal(true, queue.IsEmpty())
}
func TestLinkedQueue_Contain(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLinkedQueue_Contain")
queue := NewLinkedQueue[int]()
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
assert.Equal(true, queue.Contain(1))
assert.Equal(false, queue.Contain(4))
}

View File

@@ -0,0 +1,118 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure.
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
package datastructure
import (
"errors"
"github.com/duke-git/lancet/v2/lancetconstraints"
)
// PriorityQueue is a priority queue implemented by binary heap tree
// type T should implements Compare function in lancetconstraints.Comparator interface.
type PriorityQueue[T any] struct {
items []T
size int
comparator lancetconstraints.Comparator
}
// NewPriorityQueue return a pointer of PriorityQueue
// param `comparator` is used to compare values in the queue
func NewPriorityQueue[T any](capacity int, comparator lancetconstraints.Comparator) *PriorityQueue[T] {
return &PriorityQueue[T]{
items: make([]T, capacity+1),
size: 0,
comparator: comparator,
}
}
// IsEmpty checks if the queue is empty or not
func (q *PriorityQueue[T]) IsEmpty() bool {
return q.size == 0
}
// Size get number of items in the queue
func (q *PriorityQueue[T]) Size() int {
return q.size
}
// IsFull checks if the queue capacity is full or not
func (q *PriorityQueue[T]) IsFull() bool {
return q.size == len(q.items)-1
}
// Data return a slice of queue data
func (q *PriorityQueue[T]) Data() []T {
data := make([]T, q.size)
for i := 1; i < q.size+1; i++ {
data[i-1] = q.items[i]
}
return data
}
// Enqueue insert value into queue
func (q *PriorityQueue[T]) Enqueue(val T) error {
if q.IsFull() {
return errors.New("queue is already full.")
}
q.size++
q.items[q.size] = val
q.swim(q.size)
return nil
}
// Dequeue delete and return max value in queue
func (q *PriorityQueue[T]) Dequeue() (T, bool) {
var val T
if q.IsEmpty() {
return val, false
}
max := q.items[1]
q.swap(1, q.size)
q.size--
q.sink(1)
//set zero value for rest values of the queue
q.items[q.size+1] = val
return max, true
}
// swim when child's key is larger than parent's key, exchange them.
func (q *PriorityQueue[T]) swim(index int) {
for index > 1 && q.comparator.Compare(q.items[index/2], q.items[index]) < 0 {
q.swap(index, index/2)
index = index / 2
}
}
// sink when parent's key smaller than child's key, exchange parent's key with larger child's key.
func (q *PriorityQueue[T]) sink(index int) {
for 2*index <= q.size {
j := 2 * index
// get larger child node index
if j < q.size && q.comparator.Compare(q.items[j], q.items[j+1]) < 0 {
j++
}
// if parent larger than child, stop
if !(q.comparator.Compare(q.items[index], q.items[j]) < 0) {
break
}
q.swap(index, j)
index = j
}
}
// swap the two values at index i and j
func (q *PriorityQueue[T]) swap(i, j int) {
q.items[i], q.items[j] = q.items[j], q.items[i]
}

View File

@@ -0,0 +1,74 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func TestPriorityQueue_Enqueue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPriorityQueue_Enqueue")
comparator := &intComparator{}
pq := NewPriorityQueue[int](3, comparator)
assert.Equal(true, pq.IsEmpty())
assert.Equal(false, pq.IsFull())
err := pq.Enqueue(1)
assert.IsNil(err)
err = pq.Enqueue(2)
assert.IsNil(err)
err = pq.Enqueue(3)
assert.IsNil(err)
assert.Equal(true, pq.IsFull())
queueData := pq.Data()
assert.Equal([]int{3, 1, 2}, queueData)
}
func TestPriorityQueue_Dequeue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPriorityQueue_Dequeue")
comparator := &intComparator{}
pq := NewPriorityQueue[int](3, comparator)
_, ok := pq.Dequeue()
assert.Equal(false, ok)
err := pq.Enqueue(1)
assert.IsNil(err)
err = pq.Enqueue(2)
assert.IsNil(err)
err = pq.Enqueue(3)
assert.IsNil(err)
assert.Equal(3, pq.Size())
val, ok := pq.Dequeue()
assert.Equal(true, ok)
assert.Equal(3, val)
}

199
datastructure/set/set.go Normal file
View File

@@ -0,0 +1,199 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate.
package datastructure
// 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])
set.Add(items...)
return set
}
// NewSetFromSlice create a set from slice
func NewSetFromSlice[T comparable](items []T) Set[T] {
set := make(Set[T])
for _, item := range items {
set.Add(item)
}
return set
}
// Add items to set
func (s Set[T]) Add(items ...T) {
for _, v := range items {
s[v] = struct{}{}
}
}
// AddIfNotExist checks if item exists in the set,
// it adds the item to set and returns true if it does not exist in the set,
// or else it does nothing and returns false.
func (s Set[T]) AddIfNotExist(item T) bool {
if !s.Contain(item) {
if _, ok := s[item]; !ok {
s[item] = struct{}{}
return true
}
}
return false
}
// AddIfNotExistBy checks if item exists in the set and pass the `checker` function
// it adds the item to set and returns true if it does not exists in the set and
// function `checker` returns true, or else it does nothing and returns false.
func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool {
if !s.Contain(item) {
if checker(item) {
if _, ok := s[item]; !ok {
s[item] = struct{}{}
return true
}
}
}
return false
}
// Contain checks if set contains item or not
func (s Set[T]) Contain(item T) bool {
_, ok := s[item]
return ok
}
// ContainAll checks if set contains other set
func (s Set[T]) ContainAll(other Set[T]) bool {
for k := range other {
_, ok := s[k]
if !ok {
return false
}
}
return true
}
// Clone return a copy of set
func (s Set[T]) Clone() Set[T] {
set := NewSet[T]()
set.Add(s.Values()...)
return set
}
// Delete item of set
func (s Set[T]) Delete(items ...T) {
for _, v := range items {
delete(s, v)
}
}
// Equal checks if two set has same elements or not
func (s Set[T]) Equal(other Set[T]) bool {
if s.Size() != other.Size() {
return false
}
return s.ContainAll(other) && other.ContainAll(s)
}
// Iterate call function by every element of set
func (s Set[T]) Iterate(fn func(item T)) {
for v := range s {
fn(v)
}
}
// IsEmpty checks the set is empty or not
func (s Set[T]) IsEmpty() bool {
return len(s) == 0
}
// Size get the number of elements in set
func (s Set[T]) Size() int {
return len(s)
}
// Values return all values of set
func (s Set[T]) Values() []T {
result := make([]T, 0, len(s))
s.Iterate(func(value T) {
result = append(result, value)
})
return result
}
// Union creates a new set contain all element of set s and other
func (s Set[T]) Union(other Set[T]) Set[T] {
set := s.Clone()
set.Add(other.Values()...)
return set
}
// 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]()
s.Iterate(func(value T) {
if other.Contain(value) {
set.Add(value)
}
})
return set
}
// 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]()
s.Iterate(func(value T) {
if !other.Contain(value) {
set.Add(value)
}
})
other.Iterate(func(value T) {
if !s.Contain(value) {
set.Add(value)
}
})
return set
}
// Minus creates an 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]()
s.Iterate(func(value T) {
if !comparedSet.Contain(value) {
set.Add(value)
}
})
return set
}
// EachWithBreak iterates over elements of a set and invokes function for each element,
// when iteratee return false, will break the for each loop.
func (s Set[T]) EachWithBreak(iteratee func(item T) bool) {
for _, v := range s.Values() {
if !iteratee(v) {
break
}
}
}
// 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
}
return v, false
}

View File

@@ -0,0 +1,262 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestSet_NewSetFromSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_NewSetFromSlice")
s1 := NewSetFromSlice([]int{1, 2, 2, 3})
assert.Equal(3, s1.Size())
assert.Equal(true, s1.Contain(1))
assert.Equal(true, s1.Contain(2))
assert.Equal(true, s1.Contain(3))
s2 := NewSetFromSlice([]int{})
assert.Equal(0, s2.Size())
}
func TestSet_Add(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Add")
set := NewSet[int]()
set.Add(1, 2, 3)
cmpSet := NewSet(1, 2, 3)
assert.Equal(true, set.Equal(cmpSet))
}
func TestSet_AddIfNotExist(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_AddIfNotExist")
set := NewSet[int]()
set.Add(1, 2, 3)
assert.Equal(false, set.AddIfNotExist(1))
assert.Equal(true, set.AddIfNotExist(4))
assert.Equal(NewSet(1, 2, 3, 4), set)
}
func TestSet_AddIfNotExistBy(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_AddIfNotExistBy")
set := NewSet[int]()
set.Add(1, 2)
ok := set.AddIfNotExistBy(3, func(val int) bool {
return val%2 != 0
})
notOk := set.AddIfNotExistBy(4, func(val int) bool {
return val%2 != 0
})
assert.Equal(true, ok)
assert.Equal(false, notOk)
assert.Equal(true, set.Contain(3))
assert.Equal(false, set.Contain(4))
}
func TestSet_Contain(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Contain")
set := NewSet[int]()
set.Add(1, 2, 3)
assert.Equal(true, set.Contain(1))
assert.Equal(false, set.Contain(4))
}
func TestSet_ContainAll(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_ContainAll")
set1 := NewSet(1, 2, 3)
set2 := NewSet(1, 2)
set3 := NewSet(1, 2, 3, 4)
assert.Equal(true, set1.ContainAll(set2))
assert.Equal(false, set1.ContainAll(set3))
}
func TestSet_Clone(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Clone")
set1 := NewSet(1, 2, 3)
set2 := set1.Clone()
assert.Equal(true, set1.Size() == set2.Size())
assert.Equal(true, set1.ContainAll(set2))
}
func TestSet_Delete(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Delete")
set := NewSet[int]()
set.Add(1, 2, 3)
set.Delete(3)
assert.Equal(true, set.Equal(NewSet(1, 2)))
}
func TestSet_Equal(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Equal")
set1 := NewSet(1, 2, 3)
set2 := NewSet(1, 2, 3)
set3 := NewSet(1, 2, 3, 4)
assert.Equal(true, set1.Equal(set2))
assert.Equal(false, set1.Equal(set3))
}
func TestSet_Iterate(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Iterate")
set := NewSet(1, 2, 3)
arr := []int{}
set.Iterate(func(value int) {
arr = append(arr, value)
})
assert.Equal(3, len(arr))
}
func TestSet_IsEmpty(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_IsEmpty")
set := NewSet[int]()
assert.Equal(true, set.IsEmpty())
}
func TestSet_Size(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Size")
set := NewSet(1, 2, 3)
assert.Equal(3, set.Size())
}
func TestSet_Values(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Values")
set := NewSet(1, 2, 3)
values := set.Values()
assert.Equal(3, len(values))
}
func TestSet_Union(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Union")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
unionSet := set1.Union(set2)
assert.Equal(NewSet(1, 2, 3, 4, 5), unionSet)
}
func TestSet_Intersection(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Intersection")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
intersectionSet := set1.Intersection(set2)
assert.Equal(NewSet(2, 3), intersectionSet)
}
func TestSet_SymmetricDifference(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_SymmetricDifference")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
assert.Equal(NewSet(1, 4, 5), set1.SymmetricDifference(set2))
}
func TestSet_Minus(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Minus")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
set3 := NewSet(2, 3)
assert.Equal(NewSet(1), set1.Minus(set2))
assert.Equal(NewSet(4, 5), set2.Minus(set3))
}
func TestEachWithBreak(t *testing.T) {
// s := NewSet(1, 2, 3, 4, 5)
// var sum int
// s.EachWithBreak(func(n int) bool {
// if n > 3 {
// return false
// }
// sum += n
// return true
// })
// assert := internal.NewAssert(t, "TestEachWithBreak")
// assert.Equal(6, sum)
}
// func TestPop(t *testing.T) {
// assert := internal.NewAssert(t, "TestPop")
// s := NewSet[int]()
// val, ok := s.Pop()
// assert.Equal(0, val)
// assert.Equal(false, ok)
// s.Add(1)
// s.Add(2)
// s.Add(3)
// // s = NewSet(1, 2, 3, 4, 5)
// val, ok = s.Pop()
// assert.Equal(3, val)
// assert.Equal(true, ok)
// }

View File

@@ -0,0 +1,66 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure. Stack structure contains ArrayStack and LinkedStack.
package datastructure
import "errors"
// ArrayStack implements stack with slice
type ArrayStack[T any] struct {
data []T
length int
}
// NewArrayStack return a empty ArrayStack pointer
func NewArrayStack[T any]() *ArrayStack[T] {
return &ArrayStack[T]{data: []T{}, length: 0}
}
// Data return stack data
func (s *ArrayStack[T]) Data() []T {
return s.data
}
// Size return length of stack data
func (s *ArrayStack[T]) Size() int {
return s.length
}
// IsEmpty checks if stack is empty or not
func (s *ArrayStack[T]) IsEmpty() bool {
return s.length == 0
}
// Push element into stack
func (s *ArrayStack[T]) Push(value T) {
s.data = append([]T{value}, s.data...)
s.length++
}
// Pop delete the top element of stack then return it, if stack is empty, return nil and error
func (s *ArrayStack[T]) Pop() (*T, error) {
if s.IsEmpty() {
return nil, errors.New("stack is empty")
}
topItem := s.data[0]
s.data = s.data[1:]
s.length--
return &topItem, nil
}
// Peak return the top element of stack
func (s *ArrayStack[T]) Peak() (*T, error) {
if s.IsEmpty() {
return nil, errors.New("stack is empty")
}
return &s.data[0], nil
}
// Clear the stack data
func (s *ArrayStack[T]) Clear() {
s.data = []T{}
s.length = 0
}

View File

@@ -0,0 +1,82 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestArrayStack_Push(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayStack_Push")
stack := NewArrayStack[int]()
stack.Push(1)
stack.Push(2)
stack.Push(3)
values := stack.Data()
length := stack.Size()
assert.Equal([]int{3, 2, 1}, values)
assert.Equal(3, length)
}
func TestArrayStack_Pop(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayStack_Pop")
stack := NewArrayStack[int]()
_, err := stack.Pop()
assert.IsNotNil(err)
stack.Push(1)
stack.Push(2)
stack.Push(3)
topItem, err := stack.Pop()
assert.IsNil(err)
assert.Equal(3, *topItem)
assert.Equal([]int{2, 1}, stack.Data())
}
func TestArrayStack_Peak(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayStack_Peak")
stack := NewArrayStack[int]()
_, err := stack.Peak()
assert.IsNotNil(err)
stack.Push(1)
stack.Push(2)
stack.Push(3)
topItem, err := stack.Peak()
assert.IsNil(err)
assert.Equal(3, *topItem)
assert.Equal([]int{3, 2, 1}, stack.Data())
}
func TestArrayStack_Clear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestArrayStack_Clear")
stack := NewArrayStack[int]()
assert.Equal(true, stack.IsEmpty())
assert.Equal(0, stack.Size())
stack.Push(1)
assert.Equal(false, stack.IsEmpty())
assert.Equal(1, stack.Size())
stack.Clear()
assert.Equal(true, stack.IsEmpty())
assert.Equal(0, stack.Size())
}

View File

@@ -0,0 +1,98 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure. Stack structure contains ArrayStack and LinkedStack.
package datastructure
import (
"errors"
"fmt"
"github.com/duke-git/lancet/v2/datastructure"
)
// LinkedStack implements stack with link list
type LinkedStack[T any] struct {
top *datastructure.StackNode[T]
length int
}
// NewLinkedStack return a empty LinkedStack pointer
func NewLinkedStack[T any]() *LinkedStack[T] {
return &LinkedStack[T]{top: nil, length: 0}
}
// Data return stack data
func (s *LinkedStack[T]) Data() []T {
res := []T{}
current := s.top
for current != nil {
res = append(res, current.Value)
current = current.Next
}
return res
}
// Size return length of stack data
func (s *LinkedStack[T]) Size() int {
return s.length
}
// IsEmpty checks if stack is empty or not
func (s *LinkedStack[T]) IsEmpty() bool {
return s.length == 0
}
// Push element into stack
func (s *LinkedStack[T]) Push(value T) {
newNode := datastructure.NewStackNode(value)
top := s.top
if top == nil {
s.top = newNode
} else {
newNode.Next = top
s.top = newNode
}
s.length++
}
// Pop delete the top element of stack then return it, if stack is empty, return nil and error
func (s *LinkedStack[T]) Pop() (*T, error) {
if s.IsEmpty() {
return nil, errors.New("stack is empty")
}
top := s.top
s.top = s.top.Next
s.length--
return &top.Value, nil
}
// Peak return the top element of stack then return it
func (s *LinkedStack[T]) Peak() (*T, error) {
if s.IsEmpty() {
return nil, errors.New("stack is empty")
}
return &s.top.Value, nil
}
// Clear clear the stack data
func (s *LinkedStack[T]) Clear() {
s.top = nil
s.length = 0
}
// Print all nodes info of stack link
func (s *LinkedStack[T]) Print() {
current := s.top
info := "[ "
for current != nil {
info += fmt.Sprintf("%+v, ", current)
current = current.Next
}
info += " ]"
fmt.Println(info)
}

View File

@@ -0,0 +1,83 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestLinkedStack_Push(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLinkedStack_Push")
stack := NewLinkedStack[int]()
stack.Push(1)
stack.Push(2)
stack.Push(3)
values := stack.Data()
size := stack.Size()
assert.Equal([]int{3, 2, 1}, values)
assert.Equal(3, size)
}
func TestLinkedStack_Pop(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLinkedStack_Pop")
stack := NewLinkedStack[int]()
_, err := stack.Pop()
assert.IsNotNil(err)
stack.Push(1)
stack.Push(2)
stack.Push(3)
topItem, err := stack.Pop()
assert.IsNil(err)
assert.Equal(3, *topItem)
stack.Print()
assert.Equal([]int{2, 1}, stack.Data())
}
func TestLinkedStack_Peak(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLinkedStack_Peak")
stack := NewLinkedStack[int]()
_, err := stack.Peak()
assert.IsNotNil(err)
stack.Push(1)
stack.Push(2)
stack.Push(3)
topItem, err := stack.Peak()
assert.IsNil(err)
assert.Equal(3, *topItem)
assert.Equal([]int{3, 2, 1}, stack.Data())
}
func TestLinkedStack_Empty(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLinkedStack_Empty")
stack := NewLinkedStack[int]()
assert.Equal(true, stack.IsEmpty())
assert.Equal(0, stack.Size())
stack.Push(1)
assert.Equal(false, stack.IsEmpty())
assert.Equal(1, stack.Size())
stack.Clear()
assert.Equal(true, stack.IsEmpty())
assert.Equal(0, stack.Size())
}

View File

@@ -0,0 +1,112 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package datastructure contains some data structure. BSTree is binary search tree.
package datastructure
import (
"math"
"github.com/duke-git/lancet/v2/datastructure"
"github.com/duke-git/lancet/v2/lancetconstraints"
)
// BSTree is a binary search tree data structure in which each node has at most two children,
// which are referred to as the left child and the right child.
// In BSTree: leftNode < rootNode < rightNode
// type T should implements Compare function in lancetconstraints.Comparator interface.
type BSTree[T any] struct {
root *datastructure.TreeNode[T]
comparator lancetconstraints.Comparator
}
// NewBSTree create a BSTree pointer
// param `comparator` is used to compare values in the tree
func NewBSTree[T any](rootData T, comparator lancetconstraints.Comparator) *BSTree[T] {
root := datastructure.NewTreeNode(rootData)
return &BSTree[T]{root, comparator}
}
// InsertNode insert data into BSTree
func (t *BSTree[T]) Insert(data T) {
root := t.root
newNode := datastructure.NewTreeNode(data)
if root == nil {
t.root = newNode
} else {
insertTreeNode(root, newNode, t.comparator)
}
}
// DeletetNode delete data into BSTree
func (t *BSTree[T]) Delete(data T) {
deleteTreeNode(t.root, data, t.comparator)
}
// NodeLevel get node level in BSTree
func (t *BSTree[T]) NodeLevel(node *datastructure.TreeNode[T]) int {
if node == nil {
return 0
}
left := float64(t.NodeLevel(node.Left))
right := float64(t.NodeLevel(node.Right))
return int(math.Max(left, right)) + 1
}
// PreOrderTraverse traverse tree node in pre order
func (t *BSTree[T]) PreOrderTraverse() []T {
return preOrderTraverse(t.root)
}
// PostOrderTraverse traverse tree node in post order
func (t *BSTree[T]) PostOrderTraverse() []T {
return postOrderTraverse(t.root)
}
// InOrderTraverse traverse tree node in mid order
func (t *BSTree[T]) InOrderTraverse() []T {
return inOrderTraverse(t.root)
}
// LevelOrderTraverse traverse tree node in level order
func (t *BSTree[T]) LevelOrderTraverse() []T {
traversal := make([]T, 0)
levelOrderTraverse(t.root, &traversal)
return traversal
}
// Depth returns the calculated depth of a binary saerch tree
func (t *BSTree[T]) Depth() int {
return calculateDepth(t.root, 0)
}
// IsSubTree checks if the tree `t` has `subTree` or not
func (t *BSTree[T]) HasSubTree(subTree *BSTree[T]) bool {
return hasSubTree(t.root, subTree.root, t.comparator)
}
func hasSubTree[T any](superTreeRoot, subTreeRoot *datastructure.TreeNode[T],
comparator lancetconstraints.Comparator) bool {
result := false
if superTreeRoot != nil && subTreeRoot != nil {
if comparator.Compare(superTreeRoot.Value, subTreeRoot.Value) == 0 {
result = isSubTree(superTreeRoot, subTreeRoot, comparator)
}
if !result {
result = hasSubTree(superTreeRoot.Left, subTreeRoot, comparator)
}
if !result {
result = hasSubTree(superTreeRoot.Right, subTreeRoot, comparator)
}
}
return result
}
// Print the bstree structure
func (t *BSTree[T]) Print() {
maxLevel := t.NodeLevel(t.root)
nodes := []*datastructure.TreeNode[T]{t.root}
printTreeNodes(nodes, 1, maxLevel)
}

View File

@@ -0,0 +1,146 @@
package datastructure
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func TestBSTree_PreOrderTraverse(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBSTree_PreOrderTraverse")
bstree := NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
acturl := bstree.PreOrderTraverse()
assert.Equal([]int{6, 5, 2, 4, 7}, acturl)
}
func TestBSTree_PostOrderTraverse(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBSTree_PostOrderTraverse")
bstree := NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
acturl := bstree.PostOrderTraverse()
assert.Equal([]int{5, 2, 4, 7, 6}, acturl)
}
func TestBSTree_InOrderTraverse(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBSTree_InOrderTraverse")
bstree := NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
acturl := bstree.InOrderTraverse()
assert.Equal([]int{2, 4, 5, 6, 7}, acturl)
}
func TestBSTree_LevelOrderTraverse(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBSTree_LevelOrderTraverse")
bstree := NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
acturl := bstree.LevelOrderTraverse()
assert.Equal([]int{6, 5, 7, 2, 4}, acturl)
}
func TestBSTree_Delete(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBSTree_Delete")
bstree := NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
bstree.Delete(4)
acturl1 := bstree.InOrderTraverse()
assert.Equal([]int{2, 5, 6, 7}, acturl1)
//todo
// bstree.DeletetNode(6, comparator)
// bstree.Print()
// acturl2 := bstree.InOrderTraverse()
// assert.Equal([]int{2, 5, 7}, acturl2)
}
func TestBSTree_Depth(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBSTree_Depth")
bstree := NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
assert.Equal(bstree.Depth(), 4)
}
func TestBSTree_IsSubTree(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBSTree_IsSubTree")
superTree := NewBSTree(8, &intComparator{})
superTree.Insert(4)
superTree.Insert(5)
superTree.Insert(6)
superTree.Insert(9)
superTree.Insert(4)
superTree.Print()
subTree := NewBSTree(5, &intComparator{})
subTree.Insert(4)
subTree.Insert(6)
assert.Equal(true, superTree.HasSubTree(subTree))
assert.Equal(false, subTree.HasSubTree(superTree))
}

View File

@@ -0,0 +1,238 @@
package datastructure
import (
"fmt"
"math"
"github.com/duke-git/lancet/v2/datastructure"
"github.com/duke-git/lancet/v2/lancetconstraints"
)
func preOrderTraverse[T any](node *datastructure.TreeNode[T]) []T {
data := []T{}
if node != nil {
data = append(data, node.Value)
data = append(data, preOrderTraverse(node.Left)...)
data = append(data, preOrderTraverse(node.Right)...)
}
return data
}
func postOrderTraverse[T any](node *datastructure.TreeNode[T]) []T {
data := []T{}
if node != nil {
data = append(data, preOrderTraverse(node.Left)...)
data = append(data, preOrderTraverse(node.Right)...)
data = append(data, node.Value)
}
return data
}
func inOrderTraverse[T any](node *datastructure.TreeNode[T]) []T {
data := []T{}
if node != nil {
data = append(data, inOrderTraverse(node.Left)...)
data = append(data, node.Value)
data = append(data, inOrderTraverse(node.Right)...)
}
return data
}
// func preOrderPrint[T any](node *datastructure.TreeNode[T]) {
// if node == nil {
// return
// }
// fmt.Printf("%v, ", node.Value)
// preOrderPrint(node.Left)
// preOrderPrint(node.Right)
// }
// func postOrderPrint[T any](node *datastructure.TreeNode[T]) {
// if node == nil {
// return
// }
// postOrderPrint(node.Left)
// postOrderPrint(node.Right)
// fmt.Printf("%v, ", node.Value)
// }
// func inOrderPrint[T any](node *datastructure.TreeNode[T]) {
// if node == nil {
// return
// }
// inOrderPrint(node.Left)
// fmt.Printf("%v, ", node.Value)
// inOrderPrint(node.Right)
// }
func levelOrderTraverse[T any](root *datastructure.TreeNode[T], traversal *[]T) {
var q []*datastructure.TreeNode[T] // queue
var n *datastructure.TreeNode[T] // temp node
q = append(q, root)
for len(q) != 0 {
n, q = q[0], q[1:]
*traversal = append(*traversal, n.Value)
if n.Left != nil {
q = append(q, n.Left)
}
if n.Right != nil {
q = append(q, n.Right)
}
}
}
func insertTreeNode[T any](rootNode, newNode *datastructure.TreeNode[T], comparator lancetconstraints.Comparator) {
if comparator.Compare(newNode.Value, rootNode.Value) == -1 {
if rootNode.Left == nil {
rootNode.Left = newNode
} else {
insertTreeNode(rootNode.Left, newNode, comparator)
}
} else {
if rootNode.Right == nil {
rootNode.Right = newNode
} else {
insertTreeNode(rootNode.Right, newNode, comparator)
}
}
}
// todo, delete root node failed
func deleteTreeNode[T any](node *datastructure.TreeNode[T], data T, comparator lancetconstraints.Comparator) *datastructure.TreeNode[T] {
if node == nil {
return nil
}
if comparator.Compare(data, node.Value) == -1 {
node.Left = deleteTreeNode(node.Left, data, comparator)
} else if comparator.Compare(data, node.Value) == 1 {
node.Right = deleteTreeNode(node.Right, data, comparator)
} else {
if node.Left == nil {
node = node.Right
} else if node.Right == nil {
node = node.Left
} else {
l := node.Right
d := inOrderSuccessor(l)
d.Left = node.Left
return node.Right
}
}
return node
}
func inOrderSuccessor[T any](root *datastructure.TreeNode[T]) *datastructure.TreeNode[T] {
cur := root
for cur.Left != nil {
cur = cur.Left
}
return cur
}
func printTreeNodes[T any](nodes []*datastructure.TreeNode[T], level, maxLevel int) {
if len(nodes) == 0 || isAllNil(nodes) {
return
}
floor := maxLevel - level
endgeLines := int(math.Pow(float64(2), (math.Max(float64(floor)-1, 0))))
firstSpaces := int(math.Pow(float64(2), float64(floor))) - 1
betweenSpaces := int(math.Pow(float64(2), float64(floor)+1)) - 1
printSpaces(firstSpaces)
newNodes := []*datastructure.TreeNode[T]{}
for _, node := range nodes {
if node != nil {
fmt.Printf("%v", node.Value)
newNodes = append(newNodes, node.Left)
newNodes = append(newNodes, node.Right)
} else {
newNodes = append(newNodes, nil)
newNodes = append(newNodes, nil)
printSpaces(1)
}
printSpaces(betweenSpaces)
}
fmt.Println("")
for i := 1; i <= endgeLines; i++ {
for j := 0; j < len(nodes); j++ {
printSpaces(firstSpaces - i)
if nodes[j] == nil {
printSpaces(endgeLines + endgeLines + i + 1)
continue
}
if nodes[j].Left != nil {
fmt.Print("/")
} else {
printSpaces(1)
}
printSpaces(i + i - 1)
if nodes[j].Right != nil {
fmt.Print("\\")
} else {
printSpaces(1)
}
printSpaces(endgeLines + endgeLines - 1)
}
fmt.Println("")
}
printTreeNodes(newNodes, level+1, maxLevel)
}
// printSpaces
func printSpaces(n int) {
for i := 0; i < n; i++ {
fmt.Print(" ")
}
}
func isAllNil[T any](nodes []*datastructure.TreeNode[T]) bool {
for _, v := range nodes {
if v != nil {
return false
}
}
return true
}
func calculateDepth[T any](node *datastructure.TreeNode[T], depth int) int {
if node == nil {
return depth
}
return max(calculateDepth(node.Left, depth+1), calculateDepth(node.Right, depth+1))
}
func isSubTree[T any](superTreeRoot, subTreeRoot *datastructure.TreeNode[T], comparator lancetconstraints.Comparator) bool {
if subTreeRoot == nil {
return true
}
if superTreeRoot == nil {
return false
}
if comparator.Compare(superTreeRoot.Value, subTreeRoot.Value) != 0 {
return false
}
result := isSubTree(superTreeRoot.Left, subTreeRoot.Left, comparator) && isSubTree(superTreeRoot.Right, subTreeRoot.Right, comparator)
return result
}
func max(a, b int) int {
if a > b {
return a
}
return b
}

68
datetime/conversion.go Normal file
View File

@@ -0,0 +1,68 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license.
package datetime
import "time"
type theTime struct {
unix int64
}
// NewUnixNow return unix timestamp of current time.
// Play: https://go.dev/play/p/U4PPx-9D0oz
func NewUnixNow() *theTime {
return &theTime{unix: time.Now().Unix()}
}
// NewUnix return unix timestamp of specified time.
// Play: https://go.dev/play/p/psoSuh_kLRt
func NewUnix(unix int64) *theTime {
return &theTime{unix: unix}
}
// NewFormat return unix timestamp of specified time string, t should be "yyyy-mm-dd hh:mm:ss".
// Play: https://go.dev/play/p/VkW08ZOaXPZ
func NewFormat(t string) (*theTime, error) {
timeLayout := "2006-01-02 15:04:05"
loc := time.FixedZone("CST", 8*3600)
tt, err := time.ParseInLocation(timeLayout, t, loc)
if err != nil {
return nil, err
}
return &theTime{unix: tt.Unix()}, nil
}
// NewISO8601 return unix timestamp of specified iso8601 time string.
// Play: https://go.dev/play/p/mkhOHQkdeA2
func NewISO8601(iso8601 string) (*theTime, error) {
t, err := time.ParseInLocation(time.RFC3339, iso8601, time.UTC)
if err != nil {
return nil, err
}
return &theTime{unix: t.Unix()}, nil
}
// ToUnix return unix timestamp.
// Play: https://go.dev/play/p/_LUiwAdocjy
func (t *theTime) ToUnix() int64 {
return t.unix
}
// ToFormat return the time string 'yyyy-mm-dd hh:mm:ss' of unix time.
// Play: https://go.dev/play/p/VkW08ZOaXPZ
func (t *theTime) ToFormat() string {
return time.Unix(t.unix, 0).Format("2006-01-02 15:04:05")
}
// ToFormatForTpl return the time string which format is specified tpl.
// Play: https://go.dev/play/p/nyXxXcQJ8L5
func (t *theTime) ToFormatForTpl(tpl string) string {
return time.Unix(t.unix, 0).Format(tpl)
}
// ToFormatForTpl return iso8601 time string.
// Play: https://go.dev/play/p/mkhOHQkdeA2
func (t *theTime) ToIso8601() string {
return time.Unix(t.unix, 0).Format(time.RFC3339)
}

View File

@@ -0,0 +1,55 @@
package datetime
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestToUnix(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToUnix")
tm1 := NewUnixNow()
unixTimestamp := tm1.ToUnix()
tm2 := NewUnix(unixTimestamp)
assert.Equal(tm1, tm2)
}
func TestToFormat(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToFormat")
tm, err := NewFormat("2022-03-18 17:04:05")
t.Log("TestToFormat", tm.ToFormat())
assert.IsNil(err)
}
func TestToFormatForTpl(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToFormatForTpl")
_, err := NewFormat("2022/03/18 17:04:05")
assert.IsNotNil(err)
tm, err := NewFormat("2022-03-18 17:04:05")
t.Log("TestToFormatForTpl", tm.ToFormatForTpl("2006/01/02 15:04:05"))
assert.IsNil(err)
}
func TestToIso8601(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToIso8601")
_, err := NewISO8601("2022-03-18 17:04:05")
assert.IsNotNil(err)
tm, err := NewISO8601("2006-01-02T15:04:05.999Z")
t.Log("TestToIso8601", tm.ToIso8601())
assert.IsNil(err)
}

View File

@@ -3,29 +3,34 @@
// Package datetime implements some functions to format date and time.
// Note:
// 1. `format` param in FormatTimeToStr function should be as flow:
//"yyyy-mm-dd hh:mm:ss"
//"yyyy-mm-dd hh:mm"
//"yyyy-mm-dd hh"
//"yyyy-mm-dd"
//"yyyy-mm"
//"mm-dd"
//"dd-mm-yy hh:mm:ss"
//"yyyy/mm/dd hh:mm:ss"
//"yyyy/mm/dd hh:mm"
//"yyyy/mm/dd hh"
//"yyyy/mm/dd"
//"yyyy/mm"
//"mm/dd"
//"dd/mm/yy hh:mm:ss"
//"yyyy"
//"mm"
//"hh:mm:ss"
//"mm:ss"
// 1. `format` param in FormatTimeToStr function should be as flow (case no sensitive):
// "yyyy-mm-dd hh:mm:ss"
// "yyyy-mm-dd hh:mm"
// "yyyy-mm-dd hh"
// "yyyy-mm-dd"
// "yyyy-mm"
// "mm-dd"
// "dd-mm-yy hh:mm:ss"
// "yyyy/mm/dd hh:mm:ss"
// "yyyy/mm/dd hh:mm"
// "yyyy/mm/dd hh"
// "yyyy/mm/dd"
// "yyyy/mm"
// "mm/dd"
// "dd/mm/yy hh:mm:ss"
// "yyyymmdd"
// "mmddyy"
// "yyyy"
// "yy"
// "mm"
// "hh:mm:ss"
// "hh:mm"
// "mm:ss"
package datetime
import (
"fmt"
"strings"
"time"
)
@@ -35,7 +40,7 @@ func init() {
timeFormat = map[string]string{
"yyyy-mm-dd hh:mm:ss": "2006-01-02 15:04:05",
"yyyy-mm-dd hh:mm": "2006-01-02 15:04",
"yyyy-mm-dd hh": "2006-01-02 15:04",
"yyyy-mm-dd hh": "2006-01-02 15",
"yyyy-mm-dd": "2006-01-02",
"yyyy-mm": "2006-01",
"mm-dd": "01-02",
@@ -47,136 +52,332 @@ func init() {
"yyyy/mm": "2006/01",
"mm/dd": "01/02",
"dd/mm/yy hh:mm:ss": "02/01/06 15:04:05",
"yyyymmdd": "20060102",
"mmddyy": "010206",
"yyyy": "2006",
"yy": "06",
"mm": "01",
"hh:mm:ss": "15:04:05",
"hh:mm": "15:04",
"mm:ss": "04:05",
}
}
// AddMinute add or sub minute to the time
// AddMinute add or sub minute to the time.
// Play: https://go.dev/play/p/nT1heB1KUUK
func AddMinute(t time.Time, minute int64) time.Time {
return t.Add(time.Minute * time.Duration(minute))
}
// AddHour add or sub hour to the time
// AddHour add or sub hour to the time.
// Play: https://go.dev/play/p/rcMjd7OCsi5
func AddHour(t time.Time, hour int64) time.Time {
return t.Add(time.Hour * time.Duration(hour))
}
// AddDay add or sub day to the time
// AddDay add or sub day to the time.
// Play: https://go.dev/play/p/dIGbs_uTdFa
func AddDay(t time.Time, day int64) time.Time {
return t.Add(24 * time.Hour * time.Duration(day))
}
// GetNowDate return format yyyy-mm-dd of current date
// AddYear add or sub year to the time.
// Play: https://go.dev/play/p/MqW2ujnBx10
func AddYear(t time.Time, year int64) time.Time {
return t.Add(365 * 24 * time.Hour * time.Duration(year))
}
// GetNowDate return format yyyy-mm-dd of current date.
// Play: https://go.dev/play/p/PvfkPpcpBBf
func GetNowDate() string {
return time.Now().Format("2006-01-02")
}
// GetNowTime return format hh-mm-ss of current time
// GetNowTime return format hh-mm-ss of current time.
// Play: https://go.dev/play/p/l7BNxCkTmJS
func GetNowTime() string {
return time.Now().Format("15:04:05")
}
// GetNowDateTime return format yyyy-mm-dd hh-mm-ss of current datetime
// GetNowDateTime return format yyyy-mm-dd hh-mm-ss of current datetime.
// Play: https://go.dev/play/p/pI4AqngD0al
func GetNowDateTime() string {
return time.Now().Format("2006-01-02 15:04:05")
}
// GetZeroHourTimestamp return timestamp of zero hour (timestamp of 00:00)
// GetTodayStartTime return the start time of today, format: yyyy-mm-dd 00:00:00.
// Play: https://go.dev/play/p/84siyYF7t99
func GetTodayStartTime() string {
return time.Now().Format("2006-01-02") + " 00:00:00"
}
// GetTodayEndTime return the end time of today, format: yyyy-mm-dd 23:59:59.
// Play: https://go.dev/play/p/jjrLnfoqgn3
func GetTodayEndTime() string {
return time.Now().Format("2006-01-02") + " 23:59:59"
}
// GetZeroHourTimestamp return timestamp of zero hour (timestamp of 00:00).
// Play: https://go.dev/play/p/QmL2oIaGE3q
func GetZeroHourTimestamp() int64 {
ts := time.Now().Format("2006-01-02")
t, _ := time.Parse("2006-01-02", ts)
return t.UTC().Unix() - 8*3600
}
// GetNightTimestamp return timestamp of zero hour (timestamp of 23:59)
// GetNightTimestamp return timestamp of zero hour (timestamp of 23:59).
// Play: https://go.dev/play/p/UolysR3MYP1
func GetNightTimestamp() int64 {
return GetZeroHourTimestamp() + 86400 - 1
}
// FormatTimeToStr convert time to string
func FormatTimeToStr(t time.Time, format string) string {
return t.Format(timeFormat[format])
}
// FormatStrToTime convert string to time
func FormatStrToTime(str, format string) (time.Time, error) {
v, ok := timeFormat[format]
// FormatTimeToStr convert time to string.
// Play: https://go.dev/play/p/_Ia7M8H_OvE
func FormatTimeToStr(t time.Time, format string, timezone ...string) string {
tf, ok := timeFormat[strings.ToLower(format)]
if !ok {
return time.Time{}, fmt.Errorf("format %s not found", format)
return ""
}
return time.Parse(v, str)
if timezone != nil && timezone[0] != "" {
loc, err := time.LoadLocation(timezone[0])
if err != nil {
return ""
}
return t.In(loc).Format(tf)
}
return t.Format(tf)
}
// BeginOfMinute return beginning minute time of day
// FormatStrToTime convert string to time.
// Play: https://go.dev/play/p/1h9FwdU8ql4
func FormatStrToTime(str, format string, timezone ...string) (time.Time, error) {
tf, ok := timeFormat[strings.ToLower(format)]
if !ok {
return time.Time{}, fmt.Errorf("format %s not support", format)
}
if timezone != nil && timezone[0] != "" {
loc, err := time.LoadLocation(timezone[0])
if err != nil {
return time.Time{}, err
}
return time.ParseInLocation(tf, str, loc)
}
return time.Parse(tf, str)
}
// BeginOfMinute return beginning minute time of day.
// Play: https://go.dev/play/p/ieOLVJ9CiFT
func BeginOfMinute(t time.Time) time.Time {
y, m, d := t.Date()
return time.Date(y, m, d, t.Hour(), t.Minute(), 0, 0, t.Location())
}
// EndOfMinute return end minute time of day
// EndOfMinute return end minute time of day.
// Play: https://go.dev/play/p/yrL5wGzPj4z
func EndOfMinute(t time.Time) time.Time {
y, m, d := t.Date()
return time.Date(y, m, d, t.Hour(), t.Minute(), 59, int(time.Second-time.Nanosecond), t.Location())
}
// BeginOfHour return beginning hour time of day
// BeginOfHour return beginning hour time of day.
// Play: https://go.dev/play/p/GhdGFnDWpYs
func BeginOfHour(t time.Time) time.Time {
y, m, d := t.Date()
return time.Date(y, m, d, t.Hour(), 0, 0, 0, t.Location())
}
// EndOfHour return end hour time of day
// EndOfHour return end hour time of day.
// Play: https://go.dev/play/p/6ce3j_6cVqN
func EndOfHour(t time.Time) time.Time {
y, m, d := t.Date()
return time.Date(y, m, d, t.Hour(), 59, 59, int(time.Second-time.Nanosecond), t.Location())
}
// BeginOfDay return beginning hour time of day
// BeginOfDay return beginning hour time of day.
// Play: https://go.dev/play/p/94m_UT6cWs9
func BeginOfDay(t time.Time) time.Time {
y, m, d := t.Date()
return time.Date(y, m, d, 0, 0, 0, 0, t.Location())
}
// EndOfDay return end time of day
// EndOfDay return end time of day.
// Play: https://go.dev/play/p/eMBOvmq5Ih1
func EndOfDay(t time.Time) time.Time {
y, m, d := t.Date()
return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location())
}
// BeginOfWeek return beginning week, week begin from Sunday
func BeginOfWeek(t time.Time) time.Time {
y, m, d := t.AddDate(0, 0, 0-int(BeginOfDay(t).Weekday())).Date()
return time.Date(y, m, d, 0, 0, 0, 0, t.Location())
// BeginOfWeek return beginning week, default week begin from Sunday.
// Play: https://go.dev/play/p/ynjoJPz7VNV
func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time {
var beginFromWeekday = time.Sunday
if len(beginFrom) > 0 {
beginFromWeekday = beginFrom[0]
}
y, m, d := t.AddDate(0, 0, int(beginFromWeekday-t.Weekday())).Date()
beginOfWeek := time.Date(y, m, d, 0, 0, 0, 0, t.Location())
if beginOfWeek.After(t) {
return beginOfWeek.AddDate(0, 0, -7)
}
return beginOfWeek
}
// EndOfWeek return end week time, week end with Saturday
func EndOfWeek(t time.Time) time.Time {
y, m, d := BeginOfWeek(t).AddDate(0, 0, 7).Add(-time.Nanosecond).Date()
return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location())
// EndOfWeek return end week time, default week end with Saturday.
// Play: https://go.dev/play/p/i08qKXD9flf
func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time {
var endWithWeekday = time.Saturday
if len(endWith) > 0 {
endWithWeekday = endWith[0]
}
y, m, d := t.AddDate(0, 0, int(endWithWeekday-t.Weekday())).Date()
var endWithWeek = time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location())
if endWithWeek.Before(t) {
endWithWeek = endWithWeek.AddDate(0, 0, 7)
}
return endWithWeek
}
// BeginOfMonth return beginning of month
// BeginOfMonth return beginning of month.
// Play: https://go.dev/play/p/bWXVFsmmzwL
func BeginOfMonth(t time.Time) time.Time {
y, m, _ := t.Date()
return time.Date(y, m, 1, 0, 0, 0, 0, t.Location())
}
// EndOfMonth return end of month
// EndOfMonth return end of month.
// Play: https://go.dev/play/p/_GWh10B3Nqi
func EndOfMonth(t time.Time) time.Time {
return BeginOfMonth(t).AddDate(0, 1, 0).Add(-time.Nanosecond)
}
// BeginOfYear return beginning of year
// BeginOfYear return the date time at the begin of year.
// Play: https://go.dev/play/p/i326DSwLnV8
func BeginOfYear(t time.Time) time.Time {
y, _, _ := t.Date()
return time.Date(y, time.January, 1, 0, 0, 0, 0, t.Location())
}
// EndOfYear return end of year
// EndOfYear return the date time at the end of year.
// Play: https://go.dev/play/p/G01cKlMCvNm
func EndOfYear(t time.Time) time.Time {
return BeginOfYear(t).AddDate(1, 0, 0).Add(-time.Nanosecond)
}
// IsLeapYear check if param year is leap year or not.
// Play: https://go.dev/play/p/xS1eS2ejGew
func IsLeapYear(year int) bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}
// BetweenSeconds returns the number of seconds between two times.
// Play: https://go.dev/play/p/n3YDRyfyXJu
func BetweenSeconds(t1 time.Time, t2 time.Time) int64 {
index := t2.Unix() - t1.Unix()
return index
}
// DayOfYear returns which day of the year the parameter date `t` is.
// Play: https://go.dev/play/p/0hjqhTwFNlH
func DayOfYear(t time.Time) int {
y, m, d := t.Date()
firstDay := time.Date(y, 1, 1, 0, 0, 0, 0, t.Location())
nowDate := time.Date(y, m, d, 0, 0, 0, 0, t.Location())
return int(nowDate.Sub(firstDay).Hours() / 24)
}
// IsWeekend checks if passed time is weekend or not.
// Play: https://go.dev/play/p/cupRM5aZOIY
// Deprecated Use '== Weekday' instead
func IsWeekend(t time.Time) bool {
return time.Saturday == t.Weekday() || time.Sunday == t.Weekday()
}
// NowDateOrTime return current datetime with specific format and timezone.
// Play: https://go.dev/play/p/EZ-begEjtT0
func NowDateOrTime(format string, timezone ...string) string {
tf, ok := timeFormat[strings.ToLower(format)]
if !ok {
return ""
}
if timezone != nil && timezone[0] != "" {
loc, err := time.LoadLocation(timezone[0])
if err != nil {
return ""
}
return time.Now().In(loc).Format(tf)
}
return time.Now().Format(tf)
}
// Timestamp return current second timestamp.
// Play: https://go.dev/play/p/iU5b7Vvjx6x
func Timestamp(timezone ...string) int64 {
t := time.Now()
if timezone != nil && timezone[0] != "" {
loc, err := time.LoadLocation(timezone[0])
if err != nil {
return 0
}
t = t.In(loc)
}
return t.Unix()
}
// TimestampMilli return current mill second timestamp.
// Play: https://go.dev/play/p/4gvEusOTu1T
func TimestampMilli(timezone ...string) int64 {
t := time.Now()
if timezone != nil && timezone[0] != "" {
loc, err := time.LoadLocation(timezone[0])
if err != nil {
return 0
}
t = t.In(loc)
}
return int64(time.Nanosecond) * t.UnixNano() / int64(time.Millisecond)
}
// TimestampMicro return current micro second timestamp.
// Play: https://go.dev/play/p/2maANglKHQE
func TimestampMicro(timezone ...string) int64 {
t := time.Now()
if timezone != nil && timezone[0] != "" {
loc, err := time.LoadLocation(timezone[0])
if err != nil {
return 0
}
t = t.In(loc)
}
return int64(time.Nanosecond) * t.UnixNano() / int64(time.Microsecond)
}
// TimestampNano return current nano second timestamp.
// Play: https://go.dev/play/p/A9Oq_COrcCF
func TimestampNano(timezone ...string) int64 {
t := time.Now()
if timezone != nil && timezone[0] != "" {
loc, err := time.LoadLocation(timezone[0])
if err != nil {
return 0
}
t = t.In(loc)
}
return t.UnixNano()
}

View File

@@ -0,0 +1,410 @@
package datetime
import (
"fmt"
"reflect"
"time"
)
func ExampleAddDay() {
now := time.Now()
tomorrow := AddDay(now, 1)
diff1 := tomorrow.Sub(now)
yesterday := AddDay(now, -1)
diff2 := yesterday.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
// Output:
// 24h0m0s
// -24h0m0s
}
func ExampleAddHour() {
now := time.Now()
after2Hours := AddHour(now, 2)
diff1 := after2Hours.Sub(now)
before2Hours := AddHour(now, -2)
diff2 := before2Hours.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
// Output:
// 2h0m0s
// -2h0m0s
}
func ExampleAddMinute() {
now := time.Now()
after2Minutes := AddMinute(now, 2)
diff1 := after2Minutes.Sub(now)
before2Minutes := AddMinute(now, -2)
diff2 := before2Minutes.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
// Output:
// 2m0s
// -2m0s
}
func ExampleAddYear() {
now := time.Now()
after1Year := AddYear(now, 1)
diff1 := after1Year.Sub(now)
before1Year := AddYear(now, -1)
diff2 := before1Year.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
// Output:
// 8760h0m0s
// -8760h0m0s
}
func ExampleGetNowDate() {
result := GetNowDate()
expected := time.Now().Format("2006-01-02")
fmt.Println(result == expected)
// Output:
// true
}
func ExampleGetNowTime() {
result := GetNowTime()
expected := time.Now().Format("15:04:05")
fmt.Println(result == expected)
// Output:
// true
}
func ExampleGetNowDateTime() {
result := GetNowDateTime()
expected := time.Now().Format("2006-01-02 15:04:05")
fmt.Println(result == expected)
// Output:
// true
}
// func ExampleGetZeroHourTimestamp() {
// ts := GetZeroHourTimestamp()
// fmt.Println(ts)
// // Output:
// // 1673107200
// }
// func ExampleGetNightTimestamp() {
// ts := GetNightTimestamp()
// fmt.Println(ts)
// // Output:
// // 1673193599
// }
func ExampleFormatTimeToStr() {
datetime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08")
result1 := FormatTimeToStr(datetime, "yyyy-mm-dd hh:mm:ss")
result2 := FormatTimeToStr(datetime, "yyyy-mm-dd")
result3 := FormatTimeToStr(datetime, "dd-mm-yy hh:mm:ss")
result4 := FormatTimeToStr(datetime, "yyyy-mm-dd hh")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// 2021-01-02 16:04:08
// 2021-01-02
// 02-01-21 16:04:08
// 2021-01-02 16
}
func ExampleFormatStrToTime() {
result1, _ := FormatStrToTime("2021-01-02 16:04:08", "yyyy-mm-dd hh:mm:ss")
result2, _ := FormatStrToTime("2021-01-02", "yyyy-mm-dd")
result3, _ := FormatStrToTime("02-01-21 16:04:08", "dd-mm-yy hh:mm:ss")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 2021-01-02 16:04:08 +0000 UTC
// 2021-01-02 00:00:00 +0000 UTC
// 2021-01-02 16:04:08 +0000 UTC
}
func ExampleBeginOfMinute() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := BeginOfMinute(input)
fmt.Println(result)
// Output:
// 2023-01-08 18:50:00 +0000 UTC
}
func ExampleEndOfMinute() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := EndOfMinute(input)
fmt.Println(result)
// Output:
// 2023-01-08 18:50:59.999999999 +0000 UTC
}
func ExampleBeginOfHour() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := BeginOfHour(input)
fmt.Println(result)
// Output:
// 2023-01-08 18:00:00 +0000 UTC
}
func ExampleEndOfHour() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := EndOfHour(input)
fmt.Println(result)
// Output:
// 2023-01-08 18:59:59.999999999 +0000 UTC
}
func ExampleBeginOfDay() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := BeginOfDay(input)
fmt.Println(result)
// Output:
// 2023-01-08 00:00:00 +0000 UTC
}
func ExampleEndOfDay() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := EndOfDay(input)
fmt.Println(result)
// Output:
// 2023-01-08 23:59:59.999999999 +0000 UTC
}
func ExampleBeginOfWeek() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := BeginOfWeek(input)
fmt.Println(result)
// Output:
// 2023-01-08 00:00:00 +0000 UTC
}
func ExampleEndOfWeek() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := EndOfWeek(input)
fmt.Println(result)
// Output:
// 2023-01-14 23:59:59.999999999 +0000 UTC
}
func ExampleBeginOfMonth() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := BeginOfMonth(input)
fmt.Println(result)
// Output:
// 2023-01-01 00:00:00 +0000 UTC
}
func ExampleEndOfMonth() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := EndOfMonth(input)
fmt.Println(result)
// Output:
// 2023-01-31 23:59:59.999999999 +0000 UTC
}
func ExampleBeginOfYear() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := BeginOfYear(input)
fmt.Println(result)
// Output:
// 2023-01-01 00:00:00 +0000 UTC
}
func ExampleEndOfYear() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := EndOfYear(input)
fmt.Println(result)
// Output:
// 2023-12-31 23:59:59.999999999 +0000 UTC
}
func ExampleNewUnix() {
result := NewUnix(1647597438)
fmt.Println(result)
// Output:
// &{1647597438}
}
func ExampleNewUnixNow() {
tm1 := NewUnixNow()
unixTimestamp := tm1.ToUnix()
tm2 := NewUnix(unixTimestamp)
fmt.Println(reflect.DeepEqual(tm1, tm2))
// Output:
// true
}
// func ExampleNewFormat() {
// tm, err := NewFormat("2022-03-18 17:04:05")
// if err != nil {
// return
// }
// result := tm.ToFormat()
// fmt.Println(result)
// // Output:
// // 2022-03-18 17:04:05
// }
// func ExampleNewISO8601() {
// tm, err := NewISO8601("2006-01-02T15:04:05.999Z")
// if err != nil {
// return
// }
// result := tm.ToIso8601()
// fmt.Println(result)
// // Output:
// // 2006-01-02T23:04:05+08:00
// }
func ExampleIsLeapYear() {
result1 := IsLeapYear(2000)
result2 := IsLeapYear(2001)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
func ExampleBetweenSeconds() {
today := time.Now()
tomorrow := AddDay(today, 1)
yesterday := AddDay(today, -1)
result1 := BetweenSeconds(today, tomorrow)
result2 := BetweenSeconds(today, yesterday)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 86400
// -86400
}
func ExampleDayOfYear() {
date1 := time.Date(2023, 02, 01, 1, 1, 1, 0, time.Local)
result1 := DayOfYear(date1)
date2 := time.Date(2023, 01, 02, 1, 1, 1, 0, time.Local)
result2 := DayOfYear(date2)
date3 := time.Date(2023, 01, 01, 1, 1, 1, 0, time.Local)
result3 := DayOfYear(date3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 31
// 1
// 0
}
func ExampleIsWeekend() {
date1 := time.Date(2023, 06, 03, 0, 0, 0, 0, time.Local)
date2 := time.Date(2023, 06, 04, 0, 0, 0, 0, time.Local)
date3 := time.Date(2023, 06, 02, 0, 0, 0, 0, time.Local)
result1 := IsWeekend(date1)
result2 := IsWeekend(date2)
result3 := IsWeekend(date3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// true
// true
// false
}

View File

@@ -4,10 +4,43 @@ import (
"testing"
"time"
"github.com/duke-git/lancet/internal"
"github.com/duke-git/lancet/v2/internal"
)
func TestAddYear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddDay")
now := time.Now()
after2Years := AddYear(now, 1)
diff1 := after2Years.Sub(now)
assert.Equal(float64(8760), diff1.Hours())
before2Years := AddYear(now, -1)
diff2 := before2Years.Sub(now)
assert.Equal(float64(-8760), diff2.Hours())
}
func TestBetweenSeconds(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBetweenSeconds")
today := time.Now()
tomorrow := AddDay(today, 1)
yesterday := AddDay(today, -1)
result1 := BetweenSeconds(today, tomorrow)
result2 := BetweenSeconds(today, yesterday)
assert.Equal(int64(86400), result1)
assert.Equal(int64(-86400), result2)
}
func TestAddDay(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddDay")
now := time.Now()
@@ -21,6 +54,8 @@ func TestAddDay(t *testing.T) {
}
func TestAddHour(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddHour")
now := time.Now()
@@ -34,6 +69,8 @@ func TestAddHour(t *testing.T) {
}
func TestAddMinute(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddMinute")
now := time.Now()
@@ -47,45 +84,77 @@ func TestAddMinute(t *testing.T) {
}
func TestGetNowDate(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGetNowDate")
expected := time.Now().Format("2006-01-02")
assert.Equal(expected, GetNowDate())
}
func TestGetNotTime(t *testing.T) {
assert := internal.NewAssert(t, "TestGetNotTime")
func TestGetNowTime(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGetNowTime")
expected := time.Now().Format("15:04:05")
assert.Equal(expected, GetNowTime())
}
func TestGetNowDateTime(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGetNowDateTime")
expected := time.Now().Format("2006-01-02 15:04:05")
assert.Equal(expected, GetNowDateTime())
}
func TestGetTodayStartTime(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGetTodayStartTime")
expected := time.Now().Format("2006-01-02") + " 00:00:00"
assert.Equal(expected, GetTodayStartTime())
}
func TestGetTodayEndTime(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGetTodayEndTime")
expected := time.Now().Format("2006-01-02") + " 23:59:59"
assert.Equal(expected, GetTodayEndTime())
}
func TestFormatTimeToStr(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFormatTimeToStr")
datetime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08")
cases := []string{
"yyyy-mm-dd hh:mm:ss", "yyyy-mm-dd",
"dd-mm-yy hh:mm:ss", "yyyy/mm/dd hh:mm:ss",
"hh:mm:ss", "yyyy/mm"}
"hh:mm:ss", "yyyy/mm",
"yyyy-mm-dd hh",
}
expected := []string{
"2021-01-02 16:04:08", "2021-01-02",
"02-01-21 16:04:08", "2021/01/02 16:04:08",
"16:04:08", "2021/01"}
"16:04:08", "2021/01",
"2021-01-02 16",
}
for i := 0; i < len(cases); i++ {
actual := FormatTimeToStr(datetime, cases[i])
assert.Equal(expected[i], actual)
}
ds := FormatTimeToStr(datetime, "yyyy-mm-dd hh:mm:ss", "EST")
t.Log(ds)
}
func TestFormatStrToTime(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFormatStrToTime")
formats := []string{
@@ -97,22 +166,28 @@ func TestFormatStrToTime(t *testing.T) {
"dd-mm-yy hh:mm:ss", "yyyy/mm/dd hh:mm:ss",
"yyyy/mm"}
datetimeStr := []string{
expected := []string{
"2021-01-02 16:04:08", "2021-01-02",
"02-01-21 16:04:08", "2021/01/02 16:04:08",
"2021/01"}
for i := 0; i < len(cases); i++ {
actual, err := FormatStrToTime(datetimeStr[i], cases[i])
actual, err := FormatStrToTime(expected[i], cases[i])
if err != nil {
t.Fatal(err)
}
expected, _ := time.Parse(formats[i], datetimeStr[i])
expected, _ := time.Parse(formats[i], expected[i])
assert.Equal(expected, actual)
}
estTime, err := FormatStrToTime("2021-01-02 16:04:08", "yyyy-mm-dd hh:mm:ss", "EST")
t.Log(estTime)
assert.IsNil(err)
}
func TestBeginOfMinute(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBeginOfMinute")
expected := time.Date(2022, 2, 15, 15, 48, 0, 0, time.Local)
@@ -123,6 +198,8 @@ func TestBeginOfMinute(t *testing.T) {
}
func TestEndOfMinute(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEndOfMinute")
expected := time.Date(2022, 2, 15, 15, 48, 59, 999999999, time.Local)
@@ -133,6 +210,8 @@ func TestEndOfMinute(t *testing.T) {
}
func TestBeginOfHour(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBeginOfHour")
expected := time.Date(2022, 2, 15, 15, 0, 0, 0, time.Local)
@@ -143,6 +222,8 @@ func TestBeginOfHour(t *testing.T) {
}
func TestEndOfHour(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEndOfHour")
expected := time.Date(2022, 2, 15, 15, 59, 59, 999999999, time.Local)
@@ -153,6 +234,8 @@ func TestEndOfHour(t *testing.T) {
}
func TestBeginOfDay(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBeginOfDay")
expected := time.Date(2022, 2, 15, 0, 0, 0, 0, time.Local)
@@ -163,6 +246,8 @@ func TestBeginOfDay(t *testing.T) {
}
func TestEndOfDay(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEndOfDay")
expected := time.Date(2022, 2, 15, 23, 59, 59, 999999999, time.Local)
@@ -173,6 +258,8 @@ func TestEndOfDay(t *testing.T) {
}
func TestBeginOfWeek(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBeginOfWeek")
expected := time.Date(2022, 2, 13, 0, 0, 0, 0, time.Local)
@@ -183,6 +270,8 @@ func TestBeginOfWeek(t *testing.T) {
}
func TestEndOfWeek(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEndOfWeek")
expected := time.Date(2022, 2, 19, 23, 59, 59, 999999999, time.Local)
@@ -193,6 +282,8 @@ func TestEndOfWeek(t *testing.T) {
}
func TestBeginOfMonth(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBeginOfMonth")
expected := time.Date(2022, 2, 1, 0, 0, 0, 0, time.Local)
@@ -203,6 +294,8 @@ func TestBeginOfMonth(t *testing.T) {
}
func TestEndOfMonth(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEndOfMonth")
expected := time.Date(2022, 2, 28, 23, 59, 59, 999999999, time.Local)
@@ -213,6 +306,8 @@ func TestEndOfMonth(t *testing.T) {
}
func TestBeginOfYear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBeginOfYear")
expected := time.Date(2022, 1, 1, 0, 0, 0, 0, time.Local)
@@ -223,6 +318,8 @@ func TestBeginOfYear(t *testing.T) {
}
func TestEndOfYear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEndOfYear")
expected := time.Date(2022, 12, 31, 23, 59, 59, 999999999, time.Local)
@@ -231,3 +328,85 @@ func TestEndOfYear(t *testing.T) {
assert.Equal(expected, actual)
}
func TestIsLeapYear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEndOfYear")
result1 := IsLeapYear(2000)
result2 := IsLeapYear(2001)
assert.Equal(true, result1)
assert.Equal(false, result2)
}
func TestDayOfYear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDayOfYear")
date1 := time.Date(2023, 02, 01, 1, 1, 1, 0, time.Local)
result1 := DayOfYear(date1)
assert.Equal(31, result1)
date2 := time.Date(2023, 01, 02, 1, 1, 1, 0, time.Local)
result2 := DayOfYear(date2)
assert.Equal(1, result2)
date3 := time.Date(2023, 01, 01, 1, 1, 1, 0, time.Local)
result3 := DayOfYear(date3)
assert.Equal(0, result3)
}
func TestIsWeekend(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsWeekend")
date := time.Date(2023, 06, 03, 0, 0, 0, 0, time.Local)
result := IsWeekend(date)
assert.Equal(true, result)
date1 := time.Date(2023, 06, 04, 0, 0, 0, 0, time.Local)
result1 := IsWeekend(date1)
assert.Equal(true, result1)
date2 := time.Date(2023, 06, 02, 0, 0, 0, 0, time.Local)
result2 := IsWeekend(date2)
assert.Equal(false, result2)
}
func TestNowDateOrTime(t *testing.T) {
t.Parallel()
formats := []string{
"yyyy-mm-dd hh:mm:ss",
"yyyy-mm-dd",
"dd-mm-yy hh:mm:ss",
"yyyy/mm/dd hh:mm:ss",
"hh:mm:ss",
"yyyy/mm",
"yyyy-mm-dd hh",
}
for i := 0; i < len(formats); i++ {
result := NowDateOrTime(formats[i], "UTC")
t.Log(result)
}
}
func TestTimestamp(t *testing.T) {
t.Parallel()
ts1 := Timestamp()
t.Log(ts1)
ts2 := TimestampMilli()
t.Log(ts2)
ts3 := TimestampMicro()
t.Log(ts3)
ts4 := TimestampNano()
t.Log(ts4)
}

89
docs/.vitepress/common.ts Normal file
View File

@@ -0,0 +1,89 @@
import { defineConfig, HeadConfig } from 'vitepress'
export const META_IMAGE = '/lancet_logo.png'
export const isProduction = process.env.NETLIFY && process.env.CONTEXT === 'production'
if (process.env.NETLIFY) {
console.log('Netlify build', process.env.CONTEXT)
}
const productionHead: HeadConfig[] = [
[
'script',
{
src: 'https://unpkg.com/thesemetrics@latest',
async: '',
type: 'text/javascript',
},
],
]
const rControl = /[\u0000-\u001f]/g
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g
const rCombining = /[\u0300-\u036F]/g
/**
* Default slugification function
*/
export const slugify = (str: string): string =>
str
.normalize('NFKD')
// Remove accents
.replace(rCombining, '')
// Remove control characters
.replace(rControl, '')
// Replace special characters
.replace(rSpecial, '-')
// ensure it doesn't start with a number
.replace(/^(\d)/, '_$1')
export const commonConfig = defineConfig({
title: 'Lancet',
appearance: true,
markdown: {
theme: {
dark: 'dracula-soft',
light: 'vitesse-light',
},
attrs: {
leftDelimiter: '%{',
rightDelimiter: '}%',
},
anchor: {
slugify,
},
},
head: [
// ['link', { rel: 'icon', type: 'image/svg+xml', href: '/logo.svg' }],
['link', { rel: 'icon', type: 'image/png', href: '/lancet_logo_mini.png' }],
['meta', { name: 'theme-color', content: '#5f67ee' }],
['meta', { name: 'og:type', content: 'website' }],
['meta', { name: 'og:locale', content: 'zh' }],
...(isProduction ? productionHead : []),
],
themeConfig: {
logo: { src: '/lancet_logo_mini.png', width: 24, height: 24 },
outline: [2, 3],
search: {
provider: 'local',
},
socialLinks: [
{
icon: 'github',
link: 'https://github.com/duke-git/lancet',
},
],
footer: {
copyright: 'Copyright © 2023-present Duke Du',
message: '备案号: 京ICP备2023022770号',
},
},
})

View File

@@ -0,0 +1,14 @@
import { defineConfig } from 'vitepress'
import { commonConfig } from './common'
import { zhConfig } from './zh'
import { enConfig } from './en'
// https://vitepress.dev/reference/site-config
export default defineConfig({
...commonConfig,
locales: {
root: { label: '简体中文', lang: 'zh-CN', link: '/', ...zhConfig },
en: { label: 'English', lang: 'en-US', link: '/en/', ...enConfig },
},
})

139
docs/.vitepress/en.ts Normal file
View File

@@ -0,0 +1,139 @@
import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'
export const META_URL = 'https://www.golancet.cn/en/'
export const META_TITLE = 'Lancet'
export const META_DESCRIPTION = 'A powerful util function library of Go'
export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
description: META_DESCRIPTION,
head: [
['meta', { property: 'og:url', content: META_URL }],
['meta', { property: 'og:description', content: META_DESCRIPTION }],
],
themeConfig: {
editLink: {
pattern: 'https://github.com/duke-git/lancet/edit/v2/docs/:path',
text: 'Suggest changes to this page',
},
nav: [
{
text: 'Home',
link: '/en/',
activeMatch: '^/en/',
},
{
text: 'Guide',
link: '/en/guide/introduction',
activeMatch: '^/en/guide/',
},
{ text: 'API', link: '/en/api/overview', activeMatch: '^/en/api/' },
{
text: 'Links',
items: [
{
text: 'Discussion',
link: 'https://github.com/duke-git/lancet/discussions',
},
{
text: 'Changelog',
link: 'https://github.com/duke-git/lancet/releases',
},
{
text: 'Contribution',
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTING.en-US.md',
},
],
},
],
sidebar: {
'/en/guide/': [
{
text: 'Introduction',
collapsed: false,
items: [
{
text: 'What is Lancet',
link: '/en/guide/introduction',
},
{
text: 'Getting started',
link: '/en/guide/getting_started',
},
],
},
{
text: 'Contribute Code',
collapsed: false,
items: [
{
text: 'Contribution guide',
link: '/en/guide/contribution_guide',
},
{
text: 'Contributors',
link: '/en/guide/contributors',
},
],
},
{
text: 'API Reference',
link: '/en/api/overview'
},
],
'/en/api/': [
{
text: 'Overview',
items: [{ text: 'API overview', link: '/en/api/overview' }],
},
{
text: 'Packages',
collapsed: false,
items: [
{ text: 'algorithm', link: '/en/api/packages/algorithm' },
{ text: 'compare', link: '/en/api/packages/compare' },
{ text: 'concurrency', link: '/en/api/packages/concurrency' },
{ text: 'condition', link: '/en/api/packages/condition' },
{ text: 'convertor', link: '/en/api/packages/convertor' },
{ text: 'cryptor', link: '/en/api/packages/cryptor' },
{
text: 'datastructure',
collapsed: true,
items: [
{ text: 'list', link: '/en/api/packages/datastructure/list' },
{ text: 'safelist', link: '/en/api/packages/datastructure/copyonwritelist' },
{ text: 'link', link: '/en/api/packages/datastructure/link' },
{ text: 'stack', link: '/en/api/packages/datastructure/stack' },
{ text: 'queue', link: '/en/api/packages/datastructure/queue' },
{ text: 'heap', link: '/en/api/packages/datastructure/heap' },
{ text: 'tree', link: '/en/api/packages/datastructure/tree' },
{ text: 'set', link: '/en/api/packages/datastructure/set' },
{ text: 'hashmap', link: '/en/api/packages/datastructure/hashmap' },
],
},
{ text: 'datetime', link: '/en/api/packages/datetime' },
{ text: 'fileutil', link: '/en/api/packages/fileutil' },
{ text: 'formatter', link: '/en/api/packages/formatter' },
{ text: 'function', link: '/en/api/packages/function' },
{ text: 'mathutil', link: '/en/api/packages/mathutil' },
{ text: 'maputil', link: '/en/api/packages/maputil' },
{ text: 'netutil', link: '/en/api/packages/netutil' },
{ text: 'pointer', link: '/en/api/packages/pointer' },
{ text: 'random', link: '/en/api/packages/random' },
{ text: 'retry', link: '/en/api/packages/retry' },
{ text: 'slice', link: '/en/api/packages/slice' },
{ text: 'stream', link: '/en/api/packages/stream' },
{ text: 'struct', link: '/en/api/packages/struct' },
{ text: 'strutil', link: '/en/api/packages/strutil' },
{ text: 'system', link: '/en/api/packages/system' },
{ text: 'tuple', link: '/en/api/packages/tuple' },
{ text: 'validator', link: '/en/api/packages/validator' },
{ text: 'xerror', link: '/en/api/packages/xerror' },
],
},
],
},
},
}

152
docs/.vitepress/zh.ts Normal file
View File

@@ -0,0 +1,152 @@
import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'
export const META_URL = 'https://www.golancet.cn'
export const META_TITLE = 'Lancet'
export const META_DESCRIPTION = '一个强大的Go语言工具函数库'
export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
description: META_DESCRIPTION,
head: [
['meta', { property: 'og:url', content: META_URL }],
['meta', { property: 'og:description', content: META_DESCRIPTION }],
],
themeConfig: {
editLink: {
pattern: 'https://github.com/duke-git/lancet/edit/v2/docs/:path',
text: '对本页提出修改建议',
},
outline: {
label: '本页内容',
},
docFooter: {
prev: '上一页',
next: '下一页',
},
nav: [
{
text: '首页',
link: '/',
activeMatch: '^/',
},
{
text: '指南',
link: '/guide/introduction',
activeMatch: '^/guide/',
},
{ text: 'API', link: '/api/overview', activeMatch: '^/api/' },
{
text: '相关链接',
items: [
{
text: '论坛',
link: 'https://github.com/duke-git/lancet/discussions',
},
{
text: '更新日志',
link: 'https://github.com/duke-git/lancet/releases',
},
{
text: '参与贡献',
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTING.zh-CN.md',
},
],
},
],
sidebar: {
'/guide/': [
{
text: '介绍',
collapsed: false,
items: [
{
text: 'Lancet是什么',
link: '/guide/introduction',
},
{
text: '开始',
link: '/guide/getting_started',
},
],
},
{
text: '贡献代码',
collapsed: false,
items: [
{
text: '贡献指南',
link: '/guide/contribution_guide',
},
{
text: '贡献者',
link: '/guide/contributors',
},
],
},
{
text: 'API手册',
link: '/api/overview'
},
],
'/api/': [
{
text: '概览',
items: [{ text: 'API概述', link: '/api/overview' }],
},
{
text: 'API文档',
collapsed: false,
items: [
{ text: '算法', link: '/api/packages/algorithm' },
{ text: '比较器', link: '/api/packages/compare' },
{ text: '并发处理', link: '/api/packages/concurrency' },
{ text: '条件判断', link: '/api/packages/condition' },
{ text: '类型转换', link: '/api/packages/convertor' },
{ text: '加密&解密', link: '/api/packages/cryptor' },
{
text: '数据结构',
collapsed: true,
items: [
{ text: '线性表', link: '/api/packages/datastructure/list' },
{
text: '线性表(线程安全)',
link: '/api/packages/datastructure/copyonwritelist',
},
{ text: '链表', link: '/api/packages/datastructure/link' },
{ text: '栈', link: '/api/packages/datastructure/stack' },
{ text: '队列', link: '/api/packages/datastructure/queue' },
{ text: '堆', link: '/api/packages/datastructure/heap' },
{ text: '树', link: '/api/packages/datastructure/tree' },
{ text: '集合', link: '/api/packages/datastructure/set' },
{ text: 'HashMap', link: '/api/packages/datastructure/hashmap' },
],
},
{ text: '日期&时间', link: '/api/packages/datetime' },
{ text: '文件', link: '/api/packages/fileutil' },
{ text: '格式化工具', link: '/api/packages/formatter' },
{ text: '函数', link: '/api/packages/function' },
{ text: '数学工具', link: '/api/packages/mathutil' },
{ text: 'Map', link: '/api/packages/maputil' },
{ text: '网络', link: '/api/packages/netutil' },
{ text: '指针', link: '/api/packages/pointer' },
{ text: '随机数', link: '/api/packages/random' },
{ text: '重试', link: '/api/packages/retry' },
{ text: '切片', link: '/api/packages/slice' },
{ text: '流', link: '/api/packages/stream' },
{ text: '结构体', link: '/api/packages/struct' },
{ text: '字符串', link: '/api/packages/strutil' },
{ text: '系统', link: '/api/packages/system' },
{ text: '元组', link: '/api/packages/tuple' },
{ text: '验证器', link: '/api/packages/validator' },
{ text: '错误处理', link: '/api/packages/xerror' },
],
},
],
},
},
}

69
docs/api/overview.md Normal file
View File

@@ -0,0 +1,69 @@
---
outline: deep
---
# API概述
<b>lancet柳叶刀是一个功能强大、全面、高效、可复用的go语言工具函数库。包含25个包超过600个工具函数。功能涵盖字符串处理、切片处理、网络、并发、加解密、文件处理、时间/日期、流处理、迭代器等等。</b>
<style>
.package-title {
color: black;
font-size: 18px;
text-align: center;
font-weight: bold;
}
.package-container {
font-size: 16px;
border: 1px dashed;
padding: 10px;
text-align: center;
}
.package-cell {
height: 40px;
width: 140px;
display: inline-block;
vertical-align: middle;
line-height: 40px;
background: #ecefff;
border: 1px solid;
margin-right: 10px;
margin-bottom: 10px;
border-radius: 6px;
font-weight: bold;
}
</style>
<div>
<p class="package-title">lancet功能模块</p>
<div class="package-container">
<div class="package-cell">algorithm</div>
<div class="package-cell">compare</div>
<div class="package-cell">concurrency</div>
<div class="package-cell">condition</div>
<div class="package-cell">convertor</div>
<div class="package-cell">cryptor</div>
<div class="package-cell">datastructure</div>
<div class="package-cell">datetime</div>
<div class="package-cell">fileutil</div>
<div class="package-cell">formatter</div>
<div class="package-cell">function</div>
<div class="package-cell">iterator</div>
<div class="package-cell">maputil</div>
<div class="package-cell">mathutil</div>
<div class="package-cell">netutil</div>
<div class="package-cell">pointer</div>
<div class="package-cell">random</div>
<div class="package-cell">retry</div>
<div class="package-cell">slice</div>
<div class="package-cell">stream</div>
<div class="package-cell">structs</div>
<div class="package-cell">strutil</div>
<div class="package-cell">system</div>
<div class="package-cell">tuple</div>
<div class="package-cell">validator</div>
<div class="package-cell">xerror</div>
</div>
</div>

View File

@@ -0,0 +1,636 @@
# Algorithm
algorithm 算法包实现一些基本算法sortsearchlrucache。
<div STYLE="page-break-after: always;"></div>
## 源码
- [https://github.com/duke-git/lancet/blob/main/algorithm/sort.go](https://github.com/duke-git/lancet/blob/main/algorithm/sort.go)
- [https://github.com/duke-git/lancet/blob/main/algorithm/search.go](https://github.com/duke-git/lancet/blob/main/algorithm/search.go)
- [https://github.com/duke-git/lancet/blob/main/algorithm/lru_cache.go](https://github.com/duke-git/lancet/blob/main/algorithm/lru_cache.go)
<div STYLE="page-break-after: always;"></div>
## 用法
```go
import (
"github.com/duke-git/lancet/v2/algorithm"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [BubbleSort](#BubbleSort)
- [InsertionSort](#InsertionSort)
- [SelectionSort](#SelectionSort)
- [ShellSort](#ShellSort)
- [QuickSort](#QuickSort)
- [HeapSort](#HeapSort)
- [MergeSort](#MergeSort)
- [CountSort](#CountSort)
- [BinarySearch](#BinarySearch)
- [BinaryIterativeSearch](#BinaryIterativeSearch)
- [LinearSearch](#LinearSearch)
- [LRUCache](#LRUCache)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="BubbleSort">BubbleSort</span>
<p>冒泡排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<b>函数签名:</b>
```go
func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/GNdv7Jg2Taj)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
type intComparator struct{}
func (c *intComparator) Compare(v1 any, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
//ascending order
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
algorithm.BubbleSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
```
### <span id="InsertionSort">InsertionSort</span>
<p>插入排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<b>函数签名:</b>
```go
func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/G5LJiWgJJW6)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
type people struct {
Name string
Age int
}
// PeopleAageComparator sort people slice by age field
type peopleAgeComparator struct{}
// Compare implements github.com/duke-git/lancet/lancetconstraints/constraints.go/Comparator
func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int {
p1, _ := v1.(people)
p2, _ := v2.(people)
//ascending order
if p1.Age < p2.Age {
return -1
} else if p1.Age > p2.Age {
return 1
}
return 0
}
func main() {
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{}
algorithm.InsertionSort(peoples, comparator)
fmt.Println(peoples)
// Output:
// [{d 8} {b 10} {c 17} {a 20} {e 28}]
}
```
### <span id="SelectionSort">SelectionSort</span>
<p>选择排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<b>函数签名:</b>
```go
func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/oXovbkekayS)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
type intComparator struct{}
func (c *intComparator) Compare(v1 any, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
//ascending order
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
algorithm.SelectionSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
```
### <span id="ShellSort">ShellSort</span>
<p>希尔排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<b>函数签名:</b>
```go
func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/3ibkszpJEu3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
type intComparator struct{}
func (c *intComparator) Compare(v1 any, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
//ascending order
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
algorithm.ShellSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
```
### <span id="QuickSort">QuickSort</span>
<p>快速排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<b>函数签名:</b>
```go
func QuickSort[T any](slice []T comparator lancetconstraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7Y7c1Elk3ax)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
type intComparator struct{}
func (c *intComparator) Compare(v1 any, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
//ascending order
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
algorithm.QuickSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
```
### <span id="HeapSort">HeapSort</span>
<p>堆排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<b>函数签名:</b>
```go
func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/u6Iwa1VZS_f)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
type intComparator struct{}
func (c *intComparator) Compare(v1 any, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
//ascending order
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
algorithm.HeapSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
```
### <span id="MergeSort">MergeSort</span>
<p>归并排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<b>函数签名:</b>
```go
func MergeSort[T any](slice []T, comparator lancetconstraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ydinn9YzUJn)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
type intComparator struct{}
func (c *intComparator) Compare(v1 any, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
//ascending order
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
algorithm.MergeSort(numbers, comparator)
fmt.Println(numbers)
// Output:
// [1 2 3 4 5 6]
}
```
### <span id="CountSort">CountSort</span>
<p>计数排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<b>函数签名:</b>
```go
func CountSort[T any](slice []T, comparator lancetconstraints.Comparator) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/tB-Umgm0DrP)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
type intComparator struct{}
func (c *intComparator) Compare(v1 any, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
//ascending order
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
sortedNums := algorithm.CountSort(numbers, comparator)
fmt.Println(sortedNums)
// Output:
// [1 2 3 4 5 6]
}
```
### <span id="BinarySearch">BinarySearch</span>
<p>二分递归查找,返回元素索引,未找到元素返回-1参数comparator需要实现包lancetconstraints.Comparator。</p>
<b>函数签名:</b>
```go
func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/t6MeGiUSN47)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
type intComparator struct{}
func (c *intComparator) Compare(v1 any, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
//ascending order
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
comparator := &intComparator{}
result1 := algorithm.BinarySearch(numbers, 5, 0, len(numbers)-1, comparator)
result2 := algorithm.BinarySearch(numbers, 9, 0, len(numbers)-1, comparator)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 4
// -1
}
```
### <span id="BinaryIterativeSearch">BinaryIterativeSearch</span>
<p>二分迭代查找,返回元素索引,未找到元素返回-1参数comparator需要实现包lancetconstraints.Comparator。</p>
<b>函数签名:</b>
```go
func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Anozfr8ZLH3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
type intComparator struct{}
func (c *intComparator) Compare(v1 any, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
//ascending order
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
comparator := &intComparator{}
result1 := algorithm.BinaryIterativeSearch(numbers, 5, 0, len(numbers)-1, comparator)
result2 := algorithm.BinaryIterativeSearch(numbers, 9, 0, len(numbers)-1, comparator)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 4
// -1
}
```
### <span id="LinearSearch">LinearSearch</span>
<p>基于传入的相等函数线性查找元素,返回元素索引,未找到元素返回-1。</p>
<b>函数签名:</b>
```go
func LinearSearch[T any](slice []T, target T, equal func(a, b T) bool) int
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/IsS7rgn5s3x)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
func main() {
numbers := []int{3, 4, 5, 3, 2, 1}
equalFunc := func(a, b int) bool {
return a == b
}
result1 := algorithm.LinearSearch(numbers, 3, equalFunc)
result2 := algorithm.LinearSearch(numbers, 6, equalFunc)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 0
// -1
}
```
### <span id="LRUCache">LRUCache</span>
<p>lru算法实现缓存。</p>
<b>函数签名:</b>
```go
func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V]
func (l *LRUCache[K, V]) Get(key K) (V, bool)
func (l *LRUCache[K, V]) Put(key K, value V)
func (l *LRUCache[K, V]) Delete(key K) bool
func (l *LRUCache[K, V]) Len() int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/-EZjgOURufP)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/algorithm"
)
func main() {
cache := algorithm.NewLRUCache[int, int](2)
cache.Put(1, 1)
cache.Put(2, 2)
result1, ok1 := cache.Get(1)
result2, ok2 := cache.Get(2)
result3, ok3 := cache.Get(3)
fmt.Println(result1, ok1)
fmt.Println(result2, ok2)
fmt.Println(result3, ok3)
fmt.Println(cache.Len())
ok := cache.Delete(2)
fmt.Println(ok)
// Output:
// 1 true
// 2 true
// 0 false
// 2
// true
}
```

View File

@@ -0,0 +1,375 @@
# Compare
compare包提供几个轻量级的类型比较函数。
<div STYLE="page-break-after: always;"></div>
## 源码:
- [https://github.com/duke-git/lancet/blob/main/compare/compare.go](https://github.com/duke-git/lancet/blob/main/compare/compare.go)
- [https://github.com/duke-git/lancet/blob/main/compare/compare_internal.go](https://github.com/duke-git/lancet/blob/main/compare/compare_internal.go)
<div STYLE="page-break-after: always;"></div>
## 用法:
```go
import (
"github.com/duke-git/lancet/v2/condition"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [Equal](#Equal)
- [EqualValue](#EqualValue)
- [LessThan](#LessThan)
- [GreaterThan](#GreaterThan)
- [LessOrEqual](#LessOrEqual)
- [GreaterOrEqual](#GreaterOrEqual)
- [InDelta](#InDelta)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="Equal">Equal</span>
<p>检查两个值是否相等(检查类型和值)</p>
<b>函数签名:</b>
```go
func Equal(left, right any) bool
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wmVxR-to4lz)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/compare"
)
func main() {
result1 := compare.Equal(1, 1)
result2 := compare.Equal("1", "1")
result3 := compare.Equal([]int{1, 2, 3}, []int{1, 2, 3})
result4 := compare.Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"})
result5 := compare.Equal(1, "1")
result6 := compare.Equal(1, int64(1))
result7 := compare.Equal([]int{1, 2}, []int{1, 2, 3})
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
fmt.Println(result7)
// Output:
// true
// true
// true
// true
// false
// false
// false
}
```
### <span id="EqualValue">EqualValue</span>
<p>检查两个值是否相等(只检查值)</p>
<b>函数签名:</b>
```go
func EqualValue(left, right any) bool
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/fxnna_LLD9u)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/compare"
)
func main() {
result1 := compare.EqualValue(1, 1)
result2 := compare.EqualValue(int(1), int64(1))
result3 := compare.EqualValue(1, "1")
result4 := compare.EqualValue(1, "2")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// true
// false
}
```
### <span id="LessThan">LessThan</span>
<p>验证参数`left`的值是否小于参数`right`的值。</p>
<b>函数签名:</b>
```go
func LessThan(left, right any) bool
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/cYh7FQQj0ne)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/compare"
)
func main() {
result1 := compare.LessThan(1, 2)
result2 := compare.LessThan(1.1, 2.2)
result3 := compare.LessThan("a", "b")
time1 := time.Now()
time2 := time1.Add(time.Second)
result4 := compare.LessThan(time1, time2)
result5 := compare.LessThan(2, 1)
result6 := compare.LessThan(1, int64(2))
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
// Output:
// true
// true
// true
// true
// false
// false
}
```
### <span id="GreaterThan">GreaterThan</span>
<p>验证参数`left`的值是否大于参数`right`的值。</p>
<b>函数签名:</b>
```go
func GreaterThan(left, right any) bool
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9-NYDFZmIMp)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/compare"
)
func main() {
result1 := compare.GreaterThan(2, 1)
result2 := compare.GreaterThan(2.2, 1.1)
result3 := compare.GreaterThan("b", "a")
time1 := time.Now()
time2 := time1.Add(time.Second)
result4 := compare.GreaterThan(time2, time1)
result5 := compare.GreaterThan(1, 2)
result6 := compare.GreaterThan(int64(2), 1)
result7 := compare.GreaterThan("b", "c")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
fmt.Println(result7)
// Output:
// true
// true
// true
// true
// false
// false
// false
}
```
### <span id="LessOrEqual">LessOrEqual</span>
<p>验证参数`left`的值是否小于或等于参数`right`的值。</p>
<b>函数签名:</b>
```go
func LessOrEqual(left, right any) bool
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/e4T_scwoQzp)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/compare"
)
func main() {
result1 := compare.LessOrEqual(1, 1)
result2 := compare.LessOrEqual(1.1, 2.2)
result3 := compare.LessOrEqual("a", "b")
time1 := time.Now()
time2 := time1.Add(time.Second)
result4 := compare.LessOrEqual(time1, time2)
result5 := compare.LessOrEqual(2, 1)
result6 := compare.LessOrEqual(1, int64(2))
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
// Output:
// true
// true
// true
// true
// false
// false
}
```
### <span id="GreaterOrEqual">GreaterOrEqual</span>
<p>验证参数`left`的值是否大于或参数`right`的值。</p>
<b>函数签名:</b>
```go
func GreaterOrEqual(left, right any) bool
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vx8mP0U8DFk)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/compare"
)
func main() {
result1 := compare.GreaterOrEqual(1, 1)
result2 := compare.GreaterOrEqual(2.2, 1.1)
result3 := compare.GreaterOrEqual("b", "b")
time1 := time.Now()
time2 := time1.Add(time.Second)
result4 := compare.GreaterOrEqual(time2, time1)
result5 := compare.GreaterOrEqual(1, 2)
result6 := compare.GreaterOrEqual(int64(2), 1)
result7 := compare.GreaterOrEqual("b", "c")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
fmt.Println(result7)
// Output:
// true
// true
// true
// true
// false
// false
// false
}
```
### <span id="InDelta">InDelta</span>
<p>检查增量内两个值是否相等。</p>
<b>函数签名:</b>
```go
func InDelta[T constraints.Integer | constraints.Float](left, right T, delta float64) bool
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/TuDdcNtMkjo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/compare"
)
func main() {
result1 := InDelta(1, 1, 0)
result2 := InDelta(1, 2, 0)
result3 := InDelta(2.0/3.0, 0.66667, 0.001)
result4 := InDelta(2.0/3.0, 0.0, 0.001)
result5 := InDelta(float64(74.96)-float64(20.48), 54.48, 0)
result6 := InDelta(float64(74.96)-float64(20.48), 54.48, 1e-14)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
// Output:
// true
// false
// true
// false
// false
// true
}
```

View File

@@ -0,0 +1,454 @@
# Concurrency
并发包包含一些支持并发编程的功能。例如goroutine, channel 等。
<div STYLE="page-break-after: always;"></div>
## 源码:
- [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go)
<div STYLE="page-break-after: always;"></div>
## 用法:
```go
import (
"github.com/duke-git/lancet/v2/concurrency"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
### Channel
- [NewChannel](#NewChannel)
- [Bridge](#Bridge)
- [FanIn](#FanIn)
- [Generate](#Generate)
- [Or](#Or)
- [OrDone](#OrDone)
- [Repeat](#Repeat)
- [RepeatFn](#RepeatFn)
- [Take](#Take)
- [Tee](#Tee)
<div STYLE="page-break-after: always;"></div>
## 文档
### Channel
### <span id="NewChannel">NewChannel</span>
<p>返回一个Channel指针实例</p>
<b>函数签名:</b>
```go
type Channel[T any] struct
func NewChannel[T any]() *Channel[T]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7aB4KyMMp9A)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
c := concurrency.NewChannel[int]()
}
```
### <span id="Bridge">Bridge</span>
<p>将多个channel链接到一个channel直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/qmWSy1NVF-Y)</span></b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel[int]()
genVals := func() <-chan <-chan int {
out := make(chan (<-chan int))
go func() {
defer close(out)
for i := 1; i <= 5; i++ {
stream := make(chan int, 1)
stream <- i
close(stream)
out <- stream
}
}()
return out
}
for v := range c.Bridge(ctx, genVals()) {
fmt.Println(v)
}
// Output:
// 1
// 2
// 3
// 4
// 5
}
```
### <span id="FanIn">FanIn</span>
<p>将多个channel合并为一个channel直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/2VYFMexEvTm)</span></b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel[int]()
channels := make([]<-chan int, 2)
for i := 0; i < 2; i++ {
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 2)
}
chs := c.FanIn(ctx, channels...)
for v := range chs {
fmt.Println(v) //1 1 0 0 or 0 0 1 1
}
}
```
### <span id="Generate">Generate</span>
<p>根据传入的值生成channel.</p>
<b>函数签名:</b>
```go
func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7aB4KyMMp9A)</span></b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel[int]()
intStream := c.Generate(ctx, 1, 2, 3)
fmt.Println(<-intStream)
fmt.Println(<-intStream)
fmt.Println(<-intStream)
// Output:
// 1
// 2
// 3
}
```
### <span id="Repeat">Repeat</span>
<p>返回一个channel将参数`values`重复放入channel直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/k5N_ALVmYjE)</span></b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4)
for v := range intStream {
fmt.Println(v)
}
// Output:
// 1
// 2
// 1
// 2
}
```
### <span id="RepeatFn">RepeatFn</span>
<p>返回一个channel重复执行函数fn并将结果放入返回的channel直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/4J1zAWttP85)</span></b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fn := func() string {
return "hello"
}
c := concurrency.NewChannel[string]()
intStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
for v := range intStream {
fmt.Println(v)
}
// Output:
// hello
// hello
// hello
}
```
### <span id="Or">Or</span>
<p>将一个或多个channel读取到一个channel中当任何读取channel关闭时将结束读取。</p>
<b>函数签名:</b>
```go
func (c *Channel[T]) Or(channels ...<-chan T) <-chan T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Wqz9rwioPww)</span></b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
sig := func(after time.Duration) <-chan any {
c := make(chan any)
go func() {
defer close(c)
time.Sleep(after)
}()
return c
}
start := time.Now()
c := concurrency.NewChannel[any]()
<-c.Or(
sig(1*time.Second),
sig(2*time.Second),
sig(3*time.Second),
)
fmt.Println("done after %v", time.Since(start)) //1.003s
}
```
### <span id="OrDone">OrDone</span>
<p>将一个channel读入另一个channel直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/lm_GoS6aDjo)</span></b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
for v := range c.OrDone(ctx, intStream) {
fmt.Println(v)
}
// Output:
// 1
// 1
// 1
}
```
### <span id="Take">Take</span>
<p>返回一个channel其值从另一个channel获取直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9Utt-1pDr2J)</span></b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
numbers := make(chan int, 5)
numbers <- 1
numbers <- 2
numbers <- 3
numbers <- 4
numbers <- 5
defer close(numbers)
c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, numbers, 3)
for v := range intStream {
fmt.Println(v)
}
// Output:
// 1
// 2
// 3
}
```
### <span id="Tee">Tee</span>
<p>将一个channel分成两个channel直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/3TQPKnCirrP)</span></b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel[int]()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 2)
ch1, ch2 := c.Tee(ctx, intStream)
for v := range ch1 {
fmt.Println(v)
fmt.Println(<-ch2)
}
// Output:
// 1
// 1
// 1
// 1
}
```

View File

@@ -0,0 +1,298 @@
# Condition
condition包含一些用于条件判断的函数。这个包的实现参考了carlmjohnson的truthy包的实现更多有用的信息可以在[truthy](https://github.com/carlmjohnson/truthy)中找到感谢carlmjohnson。
<div STYLE="page-break-after: always;"></div>
## 源码:
- [https://github.com/duke-git/lancet/blob/main/condition/condition.go](https://github.com/duke-git/lancet/blob/main/condition/condition.go)
<div STYLE="page-break-after: always;"></div>
## 用法:
```go
import (
"github.com/duke-git/lancet/v2/condition"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [Bool](#Bool)
- [And](#And)
- [Or](#Or)
- [Xor](#Generate)
- [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand)
- [TernaryOperator](#TernaryOperator)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="Bool">Bool</span>
<p>返回传入参数的bool值.<br/>
如果出入类型参数含有Bool方法, 会调用该方法并返回<br/>
如果传入类型参数有IsZero方法, 返回IsZero方法返回值的取反<br/>
slices和map的length大于0时返回true否则返回false<br/>
其他类型会判断是否是零值</p>
<b>函数签名:</b>
```go
func Bool[T any](value T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ETzeDJRSvhm)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
// bool
result1 := condition.Bool(false)
result2 := condition.Bool(true)
fmt.Println(result1) // false
fmt.Println(result2) // true
// integer
result3 := condition.Bool(0)
result4 := condition.Bool(1)
fmt.Println(result3) // false
fmt.Println(result4) // true
// string
result5 := condition.Bool("")
result6 := condition.Bool(" ")
fmt.Println(result5) // false
fmt.Println(result6) // true
// slice
nums := []int{}
result7 := condition.Bool(nums)
nums = append(nums, 1, 2)
result8 := condition.Bool(nums)
fmt.Println(result7) // false
fmt.Println(result8) // true
// struct
result9 = condition.Bool(struct{}{})
fmt.Println(result8) // false
// Output:
// false
// true
// false
// true
// false
// true
// false
// true
// false
}
```
### <span id="And">And</span>
<p>逻辑且操作当切仅当a和b都为true时返回true</p>
<b>函数签名:</b>
```go
func And[T, U any](a T, b U) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/W1SSUmt6pvr)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
fmt.Println(condition.And(0, 1)) // false
fmt.Println(condition.And(0, "")) // false
fmt.Println(condition.And(0, "0")) // false
fmt.Println(condition.And(1, "0")) // true
}
```
### <span id="Or">Or</span>
<p>逻辑或操作当切仅当a和b都为false时返回false</p>
<b>函数签名:</b>
```go
func Or[T, U any](a T, b U) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/UlQTxHaeEkq)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
fmt.Println(condition.Or(0, "")) // false
fmt.Println(condition.Or(0, 1)) // true
fmt.Println(condition.Or(0, "0")) // true
fmt.Println(condition.Or(1, "0")) // true
}
```
### <span id="Xor">Xor</span>
<p>逻辑异或操作a和b相同返回falsea和b不相同返回true</p>
<b>函数签名:</b>
```go
func Xor[T, U any](a T, b U) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gObZrW7ZbG8)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
fmt.Println(condition.Xor(0, 0)) // false
fmt.Println(condition.Xor(0, 1)) // true
fmt.Println(condition.Xor(1, 0)) // true
fmt.Println(condition.Xor(1, 1)) // false
}
```
### <span id="Nor">Nor</span>
<p>异或的取反操作</p>
<b>函数签名:</b>
```go
func Nor[T, U any](a T, b U) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/g2j08F_zZky)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
fmt.Println(condition.Nor(0, 0)) // true
fmt.Println(condition.Nor(0, 1)) // false
fmt.Println(condition.Nor(1, 0)) // false
fmt.Println(condition.Nor(1, 1)) // false
}
```
### <span id="Xnor">Xnor</span>
<p>如果a和b都是真的或a和b均是假的则返回true。</p>
<b>函数签名:</b>
```go
func Xnor[T, U any](a T, b U) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/OuDB9g51643)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
fmt.Println(condition.Xnor(0, 0)) // true
fmt.Println(condition.Xnor(0, 1)) // false
fmt.Println(condition.Xnor(1, 0)) // false
fmt.Println(condition.Xnor(1, 1)) // true
}
```
### <span id="Nand">Nand</span>
<p>如果a和b都为真返回false否则返回true</p>
<b>函数签名:</b>
```go
func Nand[T, U any](a T, b U) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vSRMLxLIbq8)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
fmt.Println(condition.Nand(0, 0)) // true
fmt.Println(condition.Nand(0, 1)) // true
fmt.Println(condition.Nand(1, 0)) // true
fmt.Println(condition.Nand(1, 1)) // false
}
```
### <span id="TernaryOperator">TernaryOperator</span>
<p>三元运算符</p>
<b>函数签名:</b>
```go
func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ElllPZY0guT)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
conditionTrue := 2 > 1
result1 := condition.TernaryOperator(conditionTrue, 0, 1)
conditionFalse := 2 > 3
result2 := condition.TernaryOperator(conditionFalse, 0, 1)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 0
// 1
}
```

View File

@@ -0,0 +1,878 @@
# Convertor
convertor 转换器包支持一些常见的数据类型转换
<div STYLE="page-break-after: always;"></div>
## 源码:
- [https://github.com/duke-git/lancet/blob/main/convertor/convertor.go](https://github.com/duke-git/lancet/blob/main/convertor/convertor.go)
<div STYLE="page-break-after: always;"></div>
## 用法:
```go
import (
"github.com/duke-git/lancet/v2/convertor"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [ColorHexToRGB](#ColorHexToRGB)
- [ColorRGBToHex](#ColorRGBToHex)
- [ToBool](#ToBool)
- [ToBytes](#ToBytes)
- [ToChar](#ToChar)
- [ToChannel](#ToChannel)
- [ToFloat](#ToFloat)
- [ToInt](#ToInt)
- [ToJson](#ToJson)
- [ToMap](#ToMap)
- [ToPointer](#ToPointer)
- [ToString](#ToString)
- [StructToMap](#StructToMap)
- [MapToSlice](#MapToSlice)
- [EncodeByte](#EncodeByte)
- [DecodeByte](#DecodeByte)
- [DeepClone](#DeepClone)
- [CopyProperties](#CopyProperties)
- [ToInterface](#ToInterface)
- [Utf8ToGbk](#Utf8ToGbk)
- [GbkToUtf8](#GbkToUtf8)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="ColorHexToRGB">ColorHexToRGB</span>
<p>颜色值十六进制转rgb。</p>
<b>函数签名:</b>
```go
func ColorHexToRGB(colorHex string) (red, green, blue int)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/o7_ft-JCJBV)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
colorHex := "#003366"
r, g, b := convertor.ColorHexToRGB(colorHex)
fmt.Println(r, g, b)
// Output:
// 0 51 102
}
```
### <span id="ColorRGBToHex">ColorRGBToHex</span>
<p>颜色值rgb转十六进制。</p>
<b>函数签名:</b>
```go
func ColorRGBToHex(red, green, blue int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/nzKS2Ro87J1)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
r := 0
g := 51
b := 102
colorHex := ColorRGBToHex(r, g, b)
fmt.Println(colorHex)
// Output:
// #003366
}
```
### <span id="ToBool">ToBool</span>
<p>字符串转布尔类型使用strconv.ParseBool。</p>
<b>函数签名:</b>
```go
func ToBool(s string) (bool, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ARht2WnGdIN)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"}
for i := 0; i < len(cases); i++ {
result, _ := convertor.ToBool(cases[i])
fmt.Println(result)
}
// Output:
// true
// true
// true
// false
// false
// false
// false
// false
// false
}
```
### <span id="ToBytes">ToBytes</span>
<p>Interface转字节切片。</p>
<b>函数签名:</b>
```go
func ToBytes(data any) ([]byte, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/fAMXYFDvOvr)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
bytesData, err := convertor.ToBytes("abc")
if err != nil {
fmt.Println(err)
}
fmt.Println(bytesData)
// Output:
// [97 98 99]
}
```
### <span id="ToChar">ToChar</span>
<p>字符串转字符切片。</p>
<b>函数签名:</b>
```go
func ToChar(s string) []string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/JJ1SvbFkVdM)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
result1 := convertor.ToChar("")
result2 := convertor.ToChar("abc")
result3 := convertor.ToChar("1 2#3")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// []
// [a b c]
// [1 2 # 3]
}
```
### <span id="ToChannel">ToChannel</span>
<p>将切片转为只读channel。</p>
<b>函数签名:</b>
```go
func ToChannel[T any](array []T) <-chan T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/hOx_oYZbAnL)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
ch := convertor.ToChannel([]int{1, 2, 3})
result1 := <-ch
result2 := <-ch
result3 := <-ch
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 1
// 2
// 3
}
```
### <span id="ToFloat">ToFloat</span>
<p>将interface转成float64类型如果参数无法转换会返回0和error。</p>
<b>函数签名:</b>
```go
func ToFloat(value any) (float64, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/4YTmPCibqHJ)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
result1, _ := convertor.ToFloat("")
result2, err := convertor.ToFloat("abc")
result3, _ := convertor.ToFloat("-1")
result4, _ := convertor.ToFloat("-.11")
result5, _ := convertor.ToFloat("1.23e3")
result6, _ := convertor.ToFloat(true)
fmt.Println(result1)
fmt.Println(result2, err)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
// Output:
// 0
// 0 strconv.ParseFloat: parsing "": invalid syntax
// -1
// -0.11
// 1230
// 0
}
```
### <span id="ToInt">ToInt</span>
<p>将interface转成int64类型如果参数无法转换会返回0和error。</p>
<b>函数签名:</b>
```go
func ToInt(value any) (int64, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9_h9vIt-QZ_b)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
result1, _ := convertor.ToInt("123")
result2, _ := convertor.ToInt("-123")
result3, _ := convertor.ToInt(float64(12.3))
result4, err := convertor.ToInt("abc")
result5, _ := convertor.ToInt(true)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4, err)
fmt.Println(result5)
// Output:
// 123
// -123
// 12
// 0 strconv.ParseInt: parsing "": invalid syntax
// 0
}
```
### <span id="ToJson">ToJson</span>
<p>将interface转成json字符串如果参数无法转换会返回""和error。</p>
<b>函数签名:</b>
```go
func ToJson(value any) (string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/2rLIkMmXWvR)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result, err := ToJson(aMap)
if err != nil {
fmt.Printf("%v", err)
}
fmt.Println(result)
// Output:
// {"a":1,"b":2,"c":3}
}
```
### <span id="ToMap">ToMap</span>
<p>将切片转为map。</p>
<b>函数签名:</b>
```go
func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/tVFy7E-t24l)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
type Message struct {
name string
code int
}
messages := []Message{
{name: "Hello", code: 100},
{name: "Hi", code: 101},
}
result := convertor.ToMap(messages, func(msg Message) (int, string) {
return msg.code, msg.name
})
fmt.Println(result)
// Output:
// map[100:Hello 101:Hi]
}
```
### <span id="ToPointer">ToPointer</span>
<p>返回传入值的指针。</p>
<b>函数签名:</b>
```go
func ToPointer[T any](value T) *T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ASf_etHNlw1)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
result := convertor.ToPointer(123)
fmt.Println(*result)
// Output:
// 123
}
```
### <span id="ToString">ToString</span>
<p>将值转换为字符串,对于数字、字符串、[]byte将转换为字符串。 对于其他类型(切片、映射、数组、结构体)将调用 json.Marshal</p>
<b>函数签名:</b>
```go
func ToString(value any) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/nF1zOOslpQq)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
result1 := convertor.ToString("")
result2 := convertor.ToString(nil)
result3 := convertor.ToString(0)
result4 := convertor.ToString(1.23)
result5 := convertor.ToString(true)
result6 := convertor.ToString(false)
result7 := convertor.ToString([]int{1, 2, 3})
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
fmt.Println(result6)
fmt.Println(result7)
// Output:
//
//
// 0
// 1.23
// true
// false
// [1,2,3]
}
```
### <span id="StructToMap">StructToMap</span>
<p>将struct转成map只会转换struct中可导出的字段。struct中导出字段需要设置json tag标记。</p>
<b>函数签名:</b>
```go
func StructToMap(value any) (map[string]any, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/KYGYJqNUBOI)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
type People struct {
Name string `json:"name"`
age int
}
p := People{
"test",
100,
}
pm, _ := convertor.StructToMap(p)
fmt.Println(pm)
// Output:
// map[name:test]
}
```
### <span id="MapToSlice">MapToSlice</span>
<p>map中key和value执行函数iteratee后转为切片。</p>
<b>函数签名:</b>
```go
func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dmX4Ix5V6Wl)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result := MapToSlice(aMap, func(key string, value int) string {
return key + ":" + strconv.Itoa(value)
})
fmt.Println(result) //[]string{"a:1", "b:2", "c:3"}
}
```
### <span id="EncodeByte">EncodeByte</span>
<p>将data编码成字节切片。</p>
<b>函数签名:</b>
```go
func EncodeByte(data any) ([]byte, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/DVmM1G5JfuP)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
byteData, _ := convertor.EncodeByte("abc")
fmt.Println(byteData)
// Output:
// [6 12 0 3 97 98 99]
}
```
### <span id="DecodeByte">DecodeByte</span>
<p>解码字节切片到目标对象,目标对象需要传入一个指针实例。</p>
<b>函数签名:</b>
```go
func DecodeByte(data []byte, target any) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/zI6xsmuQRbn)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
var result string
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
err := convertor.DecodeByte(byteData, &result)
if err != nil {
return
}
fmt.Println(result)
// Output:
// abc
}
```
### <span id="DeepClone">DeepClone</span>
<p>创建一个传入值的深拷贝, 无法克隆结构体的非导出字段。</p>
<b>函数签名:</b>
```go
func DeepClone[T any](src T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](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>
<p>拷贝不同结构体之间的同名字段。使用json.Marshal序列化需要设置dst和src struct字段的json tag。</p>
<b>函数签名:</b>
```go
func CopyProperties[T, U any](dst T, src U) (err error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/oZujoB5Sgg5)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
type Disk struct {
Name string `json:"name"`
Total string `json:"total"`
Used string `json:"used"`
Percent float64 `json:"percent"`
}
type DiskVO struct {
Name string `json:"name"`
Total string `json:"total"`
Used string `json:"used"`
Percent float64 `json:"percent"`
}
type Indicator struct {
Id string `json:"id"`
Ip string `json:"ip"`
UpTime string `json:"upTime"`
LoadAvg string `json:"loadAvg"`
Cpu int `json:"cpu"`
Disk []Disk `json:"disk"`
Stop chan bool `json:"-"`
}
type IndicatorVO struct {
Id string `json:"id"`
Ip string `json:"ip"`
UpTime string `json:"upTime"`
LoadAvg string `json:"loadAvg"`
Cpu int64 `json:"cpu"`
Disk []DiskVO `json:"disk"`
}
indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{
{Name: "disk-001", Total: "100", Used: "1", Percent: 10},
{Name: "disk-002", Total: "200", Used: "1", Percent: 20},
{Name: "disk-003", Total: "300", Used: "1", Percent: 30},
}}
indicatorVO := IndicatorVO{}
err := convertor.CopyProperties(&indicatorVO, indicator)
if err != nil {
return
}
fmt.Println(indicatorVO.Id)
fmt.Println(indicatorVO.Ip)
fmt.Println(len(indicatorVO.Disk))
// Output:
// 001
// 127.0.0.1
// 3
}
```
### <span id="ToInterface">ToInterface</span>
<p>将反射值转换成对应的interface类型。</p>
<b>函数签名:</b>
```go
func ToInterface(v reflect.Value) (value interface{}, ok bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](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>utf8编码转GBK编码。</p>
<b>函数签名:</b>
```go
func Utf8ToGbk(bs []byte) ([]byte, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9FlIaFLArIL)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
utf8Data := []byte("hello")
gbkData, _ := convertor.Utf8ToGbk(utf8Data)
fmt.Println(utf8.Valid(utf8Data))
fmt.Println(validator.IsGBK(gbkData))
// Output:
// true
// true
}
```
### <span id="GbkToUtf8">GbkToUtf8</span>
<p>GBK编码转utf8编码。</p>
<b>函数签名:</b>
```go
func GbkToUtf8(bs []byte) ([]byte, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/OphmHCN_9u8)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
gbkData, _ := convertor.Utf8ToGbk([]byte("hello"))
utf8Data, _ := convertor.GbkToUtf8(gbkData)
fmt.Println(utf8.Valid(utf8Data))
fmt.Println(string(utf8Data))
// Output:
// true
// hello
}
```

1425
docs/api/packages/cryptor.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,471 @@
# CopyOnWriteList
CopyOnWriteList 是一个线程安全的 List 实现,底层使用 go 切片。写入时,会复制一份新的切片,写入完成后,再将新的切片赋值给原来的切片。读取时,直接读取原来的切片。
## 源码
- [https://github.com/duke-git/lancet/blob/main/datastructure/list/copyonwritelist.go](https://github.com/duke-git/lancet/blob/main/datastructure/list/copyonwritelist.go)
## 用法
```go
import (
"github.com/duke-git/lancet/datastructure/list"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [NewCopyOnWriteList](#NewCopyOnWriteList)
- [Size](#Size)
- [Get](#Get)
- [Set](#Set)
- [Remove](#Remove)
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [IsEmpty](#IsEmpty)
- [Contain](#Contain)
- [ValueOf](#ValueOf)
- [Add](#Add)
- [AddAll](#AddAll)
- [AddByIndex](#AddByIndex)
- [DeleteAt](#DeleteAt)
- [DeleteIf](#DeleteIf)
- [DeleteBy](#DeleteBy)
- [DeleteRange](#DeleteRange)
- [Equal](#Equal)
## 文档
### NewCopyOnWriteList
返回一个具有空切片的 CopyOnWriteList。
```go
type CopyOnWriteList[T any] struct {
data []T
lock sync.Locker
}
func NewCopyOnWriteList() *CopyOnWriteList
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
fmt.Println(l)
}
```
### Size
返回 CopyOnWriteList 的长度。
```go
func (l *CopyOnWriteList[T]) Size() int
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
fmt.Println(l.Size())
}
```
### Get
返回列表中指定位置的元素
```go
func (c *CopyOnWriteList[T]) Get(index int) *T
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
fmt.Println(l.Get(2))
}
```
### Set
将此列表中指定位置的元素替换为指定元素。
```go
func (c *CopyOnWriteList[T]) Set(index int, e T) (oldValue *T, ok bool)
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
fmt.Println(l.Set(2, 4))
}
```
### Remove
### IndexOf
返回列表中值的索引,如果没有找到返回-1。
```go
func (c *CopyOnWriteList[T]) IndexOf(e T) int
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
fmt.Println(l.IndexOf(1))
}
```
### LastIndexOf
返回指定元素在此列表中最后出现的索引,如果此列表不包含该元素,则返回-1。
```go
func (c *CopyOnWriteList[T]) LastIndexOf(e T) int
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3,1})
fmt.Println(l.LastIndexOf(1))
}
```
### IsEmpty
如果此列表不包含任何元素,则返回 true。
```go
func (c *CopyOnWriteList[T]) IsEmpty() bool
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{})
fmt.Println(l.IsEmpty())
}
```
### Contain
判断 CopyOnWriteList 是否包含某个元素
```go
func (c *CopyOnWriteList[T]) Contain(e T) bool
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
fmt.Println(l.Contain(1))
}
```
### ValueOf
返回列表中索引处的值指针
```go
func (c *CopyOnWriteList[T]) ValueOf(index int) []T
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
fmt.Println(l.ValueOf(2))
}
```
### Add
将指定的元素追加到此列表的末尾。
```go
func (c *CopyOnWriteList[T]) Add(e T) bool
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
l.Add(4)
fmt.Println(l.getList())
}
```
### AddAll
将指定集合中的所有元素追加到此列表的末尾
```go
func (c *CopyOnWriteList[T]) AddAll(e []T) bool
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
l.AddAll([]int{4,5,6})
fmt.Println(l.getList())
}
```
### AddByIndex
将指定元素插入此列表中的指定位置。
```go
func (c *CopyOnWriteList[T]) AddByIndex(index int, e T) bool
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
list.AddByIndex(2, 6)
fmt.Println(l.getList())
}
```
### DeleteAt
移除此列表中指定位置的元素。
```go
func (c *CopyOnWriteList[T]) DeleteAt(index int) (oldValue *T, ok bool)
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
list.DeleteAt(2)
fmt.Println(l.getList())
}
```
### DeleteIf
从此列表中删除第一个出现的指定元素(如果该元素存在)。
```go
func (c *CopyOnWriteList[T]) DeleteIf(f func(T) bool) (oldValue *T, ok bool)
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
list.DeleteIf(func(i int) bool {
return i == 2
})
fmt.Println(l.getList())
}
```
### DeleteBy
从此列表中删除第一个出现的指定元素(如果该元素存在)。
```go
func (c *CopyOnWriteList[T]) DeleteBy(e T) (*T bool)
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3})
list.DeleteBy(2)
fmt.Println(l.getList())
}
```
### DeleteRange
从该列表中删除索引介于 fromIndex(包含)和 toIndex(不包含)之间的所有元素。
(左闭右开)。
```go
func (c *CopyOnWriteList[T]) DeleteRange(start int, end int)
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3,4,5,6,7,8,9})
list.DeleteRange(2, 5)
fmt.Println(l.getList())
}
```
### Equal
如果指定的对象等于此列表,则返回 true。
```go
func (c *CopyOnWriteList[T]) Equal(e []T) bool
```
#### 示例
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1,2,3,4,5,6,7,8,9})
fmt.Println(l.Equal([]int{1,2,3,4,5,6,7,8,9}))
}
```

View File

@@ -0,0 +1,308 @@
# HashMap
HashMap 数据结构实现
<div STYLE="page-break-after: always;"></div>
## 源码
- [https://github.com/duke-git/lancet/blob/main/datastructure/hashmap/hashmap.go](https://github.com/duke-git/lancet/blob/main/datastructure/hashmap/hashmap.go)
<div STYLE="page-break-after: always;"></div>
## 用法
```go
import (
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [NewHashMap](#NewHashMap)
- [NewHashMapWithCapacity](#NewHashMapWithCapacity)
- [Get](#Get)
- [Put](#Put)
- [Delete](#Delete)
- [Contains](#Contains)
- [Iterate](#Iterate)
- [Keys](#Keys)
- [Values](#Values)
<div STYLE="page-break-after: always;"></div>
## API 文档
### <span id="NewHashMap">NewHashMap</span>
<p>新建默认容量1 &lt&lt 10的HashMap指针实例</p>
<b>函数签名:</b>
```go
func NewHashMap() *HashMap
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
fmt.Println(hm)
}
```
### <span id="NewHashMapWithCapacity">NewHashMapWithCapacity</span>
<p>新建指定容量和长度的HashMap指针实例.</p>
<b>函数签名:</b>
```go
func NewHashMapWithCapacity(size, capacity uint64) *HashMap
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMapWithCapacity(uint64(100), uint64(1000))
fmt.Println(hm)
}
```
### <span id="Get">Get</span>
<p>在hashmap中根据key获取值</p>
<b>函数签名:</b>
```go
func (hm *HashMap) Get(key any) any
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
val := hm.Get("a")
fmt.Println(val) //nil
}
```
### <span id="Put">Put</span>
<p>将key-value放入hashmap中</p>
<b>函数签名:</b>
```go
func (hm *HashMap) Put(key any, value any) any
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
val := hm.Get("a")
fmt.Println(val) //1
}
```
### <span id="Delete">Delete</span>
<p>将指定的key从hashmap中删除</p>
<b>函数签名:</b>
```go
func (hm *HashMap) Delete(key any)
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
val := hm.Get("a")
fmt.Println(val) //1
hm.Delete("a")
val = hm.Get("a")
fmt.Println(val) //nil
}
```
### <span id="Contains">Contains</span>
<p>判断hashmap中是否包含指定的key</p>
<b>函数签名:</b>
```go
func (hm *HashMap) Contains(key any) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
fmt.Println(hm.Contains("a")) //true
fmt.Println(hm.Contains("b")) //false
}
```
### <span id="Iterate">Iterate</span>
<p>迭代hashmap对每个key和value执行iteratee函数</p>
<b>函数签名:</b>
```go
func (hm *HashMap) Iterate(iteratee func(key, value any))
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
hm.Iterate(func(key, value any) {
fmt.Println(key)
fmt.Println(value)
})
}
```
### <span id="Keys">Keys</span>
<p>返回hashmap所有key的切片 (随机顺序)</p>
<b>函数签名:</b>
```go
func (hm *HashMap) Keys() []any
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
keys := hm.Keys()
fmt.Println(keys) //[]interface{"a", "b", "c"}
}
```
### <span id="Values">Values</span>
<p>返回hashmap所有值的切片 (随机顺序).</p>
<b>函数签名:</b>
```go
func (hm *HashMap) Values() []any
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := heap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
values := hm.Values()
fmt.Println(values) //[]interface{2, 1, 3}
}
```

View File

@@ -0,0 +1,364 @@
# Heap
堆,切片实现的二叉堆数据结构。
<div STYLE="page-break-after: always;"></div>
## 源码
- [https://github.com/duke-git/lancet/blob/main/datastructure/heap/maxheap.go](https://github.com/duke-git/lancet/blob/main/datastructure/heap/maxheap.go)
<div STYLE="page-break-after: always;"></div>
## 用法
```go
import (
heap "github.com/duke-git/lancet/v2/datastructure/heap"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [MaxHeap](#MaxHeap)
- [Push](#Push)
- [Pop](#Pop)
- [Peek](#Peek)
- [Data](#Data)
- [Size](#Size)
<div STYLE="page-break-after: always;"></div>
## API文档
### 1. MaxHeap
MaxHeap是通过slice实现的二叉堆树根节点的key既大于等于左子树的key值且大于等于右子树的key值。
### <span id="NewMaxHeap">NewMaxHeap</span>
<p>返回NewMaxHeap指针实例</p>
<b>函数签名:</b>
```go
type MaxHeap[T any] struct {
data []T
comparator lancetconstraints.Comparator
}
func NewMaxHeap[T any](comparator lancetconstraints.Comparator) *MaxHeap[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
heap "github.com/duke-git/lancet/v2/datastructure/heap"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
maxHeap := heap.NewMaxHeap[int](&intComparator{})
fmt.Println(maxHeap)
}
```
### <span id="Push">Push</span>
<p>向堆中插入数据</p>
<b>函数签名:</b>
```go
func (h *MaxHeap[T]) Push(value T)
```
<b>示例:</b>
```go
package main
import (
"fmt"
heap "github.com/duke-git/lancet/v2/datastructure/heap"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
maxHeap := heap.NewMaxHeap[int](&intComparator{})
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
for _, v := range values {
maxHeap.Push(v)
}
fmt.Println(maxHeap.Data()) //[]int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2}
}
```
### <span id="Pop">Pop</span>
<p>返回堆中最大值并将其从堆中删除如果堆为空返回零值并返回false</p>
<b>函数签名:</b>
```go
func (h *MaxHeap[T]) Pop() (T, bool)
```
<b>示例:</b>
```go
package main
import (
"fmt"
heap "github.com/duke-git/lancet/v2/datastructure/heap"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
maxHeap := heap.NewMaxHeap[int](&intComparator{})
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
for _, v := range values {
maxHeap.Push(v)
}
val, ok := maxHeap.Pop()
fmt.Println(val) //12
fmt.Println(ok) //true
}
```
### <span id="Peek">Peek</span>
<p>返回堆中最大值如果堆为空返回零值并返回false</p>
<b>函数签名:</b>
```go
func (h *MaxHeap[T]) Peek() (T, bool)
```
<b>示例:</b>
```go
package main
import (
"fmt"
heap "github.com/duke-git/lancet/v2/datastructure/heap"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
maxHeap := heap.NewMaxHeap[int](&intComparator{})
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
for _, v := range values {
maxHeap.Push(v)
}
val, ok := maxHeap.Peek()
fmt.Println(val) //12
fmt.Println(maxHeap.Size()) //12
}
```
### <span id="Data">Data</span>
<p>返回堆中全部元素的切片</p>
<b>函数签名:</b>
```go
func (h *MaxHeap[T]) Data() []T
```
<b>示例:</b>
```go
package main
import (
"fmt"
heap "github.com/duke-git/lancet/v2/datastructure/heap"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
maxHeap := heap.NewMaxHeap[int](&intComparator{})
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
for _, v := range values {
maxHeap.Push(v)
}
fmt.Println(maxHeap.Data()) //[]int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2}
}
```
### <span id="Size">Size</span>
<p>返回堆中元素的数量</p>
<b>函数签名:</b>
```go
func (h *MaxHeap[T]) Size() int
```
<b>示例:</b>
```go
package main
import (
"fmt"
heap "github.com/duke-git/lancet/v2/datastructure/heap"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
maxHeap := heap.NewMaxHeap[int](&intComparator{})
values := []int{6, 5, 2}
for _, v := range values {
maxHeap.Push(v)
}
fmt.Println(maxHeap.Size()) //3
}
```
### <span id="PrintStructure">PrintStructure</span>
<p>打印堆的树形结构</p>
<b>函数签名:</b>
```go
func (h *MaxHeap[T]) PrintStructure()
```
<b>示例:</b>
```go
package main
import (
"fmt"
heap "github.com/duke-git/lancet/v2/datastructure/heap"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
maxHeap := heap.NewMaxHeap[int](&intComparator{})
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
for _, v := range values {
maxHeap.Push(v)
}
fmt.Println(maxHeap.PrintStructure())
// 12
// 9 11
// 4 8 10 7
// 1 3 5 6 2
}
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,649 @@
# Set
Set 集合数据结构类似列表。Set 中元素不重复。
<div STYLE="page-break-after: always;"></div>
## 源码
- [https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go](https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go)
<div STYLE="page-break-after: always;"></div>
## 用法
```go
import (
set "github.com/duke-git/lancet/v2/datastructure/set"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [NewSet](#NewSet)
- [NewSetFromSlice](#NewSetFromSlice)
- [Values](#Values)
- [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
- [AddIfNotExistBy](#AddIfNotExistBy)
- [Delete](#Delete)
- [Contain](#Contain)
- [ContainAll](#ContainAll)
- [Clone](#Clone)
- [Size](#Size)
- [Equal](#Equal)
- [Iterate](#Iterate)
- [IsEmpty](#IsEmpty)
- [Union](#Union)
- [Intersection](#Intersection)
- [SymmetricDifference](#SymmetricDifference)
- [Minus](#Minus)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="NewSet">NewSet</span>
<p>返回Set结构体对象</p>
<b>函数签名:</b>
```go
type Set[T comparable] map[T]bool
func NewSet[T comparable](items ...T) Set[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
st := set.NewSet[int](1,2,2,3)
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="NewSetFromSlice">NewSetFromSlice</span>
<p>基于切片创建集合</p>
<b>函数签名:</b>
```go
func NewSetFromSlice[T comparable](items []T) Set[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="Values">Values</span>
<p>获取集合中所有元素的切片</p>
<b>函数签名:</b>
```go
func (s Set[T]) Values() []T
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
st := set.NewSet[int](1,2,2,3)
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="Add">Add</span>
<p>向集合中添加元素</p>
<b>函数签名:</b>
```go
func (s Set[T]) Add(items ...T)
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
st := set.NewSet[int]()
st.Add(1, 2, 3)
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="AddIfNotExist">AddIfNotExist</span>
<p>如果集合中不存在元素则添加该元素返回true, 如果集合中存在元素, 不做任何操作返回false</p>
<b>函数签名:</b>
```go
func (s Set[T]) AddIfNotExist(item T) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
st := set.NewSet[int]()
st.Add(1, 2, 3)
r1 := st.AddIfNotExist(1)
r2 := st.AddIfNotExist(4)
fmt.Println(r1) // false
fmt.Println(r2) // true
fmt.Println(st.Values()) // 1,2,3,4
}
```
### <span id="AddIfNotExistBy">AddIfNotExistBy</span>
<p>根据checker函数判断元素是否在集合中如果集合中不存在元素且checker返回true则添加该元素返回true, 否则不做任何操作返回false</p>
<b>函数签名:</b>
```go
func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
st := set.NewSet[int]()
st.Add(1, 2)
ok := st.AddIfNotExistBy(3, func(val int) bool {
return val%2 != 0
})
fmt.Println(ok) // true
notOk := st.AddIfNotExistBy(4, func(val int) bool {
return val%2 != 0
})
fmt.Println(notOk) // false
fmt.Println(st.Values()) // 1, 2, 3
}
```
### <span id="Delete">Delete</span>
<p>删除集合中元素</p>
<b>函数签名:</b>
```go
func (s Set[T]) Delete(items ...T)
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
st := set.NewSet[int]()
st.Add(1, 2, 3)
set.Delete(3)
fmt.Println(st.Values()) //1,2
}
```
### <span id="Contain">Contain</span>
<p>判断集合是否包含某个值</p>
<b>函数签名:</b>
```go
func (s Set[T]) Contain(item T) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
st := set.NewSet[int]()
st.Add(1, 2, 3)
fmt.Println(st.Contain(1)) //true
fmt.Println(st.Contain(4)) //false
}
```
### <span id="ContainAll">ContainAll</span>
<p>判断集合是否包含另一个集合</p>
<b>函数签名:</b>
```go
func (s Set[T]) ContainAll(other Set[T]) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(1, 2)
set3 := set.NewSet(1, 2, 3, 4)
fmt.Println(set1.ContainAll(set2)) //true
fmt.Println(set1.ContainAll(set3)) //false
}
```
### <span id="Size">Size</span>
<p>获取集合中元素的个数</p>
<b>函数签名:</b>
```go
func (s Set[T]) Size() int
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
set1 := set.NewSet(1, 2, 3)
fmt.Println(set1.Size()) //3
}
```
### <span id="Clone">Clone</span>
<p>克隆一个集合</p>
<b>函数签名:</b>
```go
func (s Set[T]) Clone() Set[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set1.Clone()
fmt.Println(set1.Size() == set2.Size()) //true
fmt.Println(set1.ContainAll(set2)) //true
}
```
### <span id="Equal">Equal</span>
<p>比较两个集合是否相等,包含相同元素为相等</p>
<b>函数签名:</b>
```go
func (s Set[T]) Equal(other Set[T]) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(1, 2, 3)
set3 := set.NewSet(1, 2, 3, 4)
fmt.Println(set1.Equal(set2)) //true
fmt.Println(set1.Equal(set3)) //false
}
```
### <span id="Iterate">Iterate</span>
<p>迭代结合,在每个元素上调用函数</p>
<b>函数签名:</b>
```go
func (s Set[T]) Iterate(fn func(item T))
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
set1 := set.NewSet(1, 2, 3)
arr := []int{}
set.Iterate(func(item int) {
arr = append(arr, item)
})
fmt.Println(arr) //1,2,3
}
```
### <span id="EachWithBreak">EachWithBreak</span>
<p>遍历集合的元素并为每个元素调用iteratee函数当iteratee函数返回false时终止遍历。</p>
<b>函数签名:</b>
```go
func (s Set[T]) EachWithBreak(iteratee func(item T) bool)
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
s := set.NewSet(1, 2, 3, 4, 5)
var sum int
s.EachWithBreak(func(n int) bool {
if n > 3 {
return false
}
sum += n
return true
})
fmt.Println(sum) //6
}
```
### <span id="IsEmpty">IsEmpty</span>
<p>判断集合是否为空</p>
<b>函数签名:</b>
```go
func (s Set[T]) IsEmpty() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet()
fmt.Println(set1.IsEmpty()) //false
fmt.Println(set2.IsEmpty()) //true
}
```
### <span id="Union">Union</span>
<p>求两个集合的并集</p>
<b>函数签名:</b>
```go
func (s Set[T]) Union(other Set[T]) Set[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set3 := set1.Union(set2)
fmt.Println(set3.Values()) //1,2,3,4,5
}
```
### <span id="Intersection">Intersection</span>
<p>求两个集合的交集</p>
<b>函数签名:</b>
```go
func (s Set[T]) Intersection(other Set[T]) Set[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set3 := set1.Intersection(set2)
fmt.Println(set3.Values()) //2,3
}
```
### <span id="SymmetricDifference">SymmetricDifference</span>
<p>返回一个集合,其中元素在第一个集合或第二个集合中,且不同时存在于两个集合中</p>
<b>函数签名:</b>
```go
func (s Set[T]) SymmetricDifference(other Set[T]) Set[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set3 := set1.SymmetricDifference(set2)
fmt.Println(set3.Values()) //1,4,5
}
```
### <span id="Minus">Minus</span>
<p>创建一个集合,其元素在原始集中但不在比较集中</p>
<b>函数签名:</b>
```go
func (s Set[T]) Minus(comparedSet Set[T]) Set[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set3 := set.NewSet(2, 3)
res1 := set1.Minus(set2)
fmt.Println(res1.Values()) //1
res2 := set2.Minus(set3)
fmt.Println(res2.Values()) //4,5
}
```
### <span id="Pop">Pop</span>
<p>删除并返回集合中的顶部元素</p>
<b>函数签名:</b>
```go
func (s Set[T]) Pop() (v T, ok bool)
```
<b>示例:</b>
```go
package main
import (
"fmt"
set "github.com/duke-git/lancet/v2/datastructure/set"
)
func main() {
s := set.NewSet[int]()
s.Add(1)
s.Add(2)
s.Add(3)
val, ok = s.Pop()
fmt.Println(val) // 3
fmt.Println(ok) // true
}
```

View File

@@ -0,0 +1,611 @@
# Stack
栈数据结构包括ArrayStack数组栈和LinkedStack链表栈
<div STYLE="page-break-after: always;"></div>
## 源码
- [https://github.com/duke-git/lancet/blob/main/datastructure/stack/arraystack.go](https://github.com/duke-git/lancet/blob/main/datastructure/stack/arraystack.go)
- [https://github.com/duke-git/lancet/blob/main/datastructure/stack/linkedstack.go](https://github.com/duke-git/lancet/blob/main/datastructure/stack/linkedstack.go)
<div STYLE="page-break-after: always;"></div>
## 用法
```go
import (
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
### 1. ArrayStack数组栈
- [NewArrayStack](#NewArrayStack)
- [Push](#ArrayStack_Push)
- [Pop](#ArrayStack_Pop)
- [Peak](#ArrayStack_Peak)
- [Data](#ArrayStack_Data)
- [Size](#ArrayStack_Size)
- [IsEmpty](#ArrayStack_IsEmpty)
- [Clear](#ArrayStack_Clear)
### 2. LinkedStack链表栈
- [NewLinkedStack](#NewLinkedStack)
- [Push](#LinkedStack_Push)
- [Pop](#LinkedStack_Pop)
- [Peak](#LinkedStack_Peak)
- [Data](#LinkedStack_Data)
- [Size](#LinkedStack_Size)
- [IsEmpty](#LinkedStack_IsEmpty)
- [Clear](#LinkedStack_Clear)
- [Print](#LinkedStack_Print)
<div STYLE="page-break-after: always;"></div>
## 文档
### 1. ArrayStack
用切片实现栈结构
### <span id="NewArrayStack">NewArrayStack</span>
<p>返回ArrayStack指针实例</p>
<b>函数签名:</b>
```go
type ArrayStack[T any] struct {
data []T
length int
}
func NewArrayStack[T any]() *ArrayStack[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewArrayStack[int]()
fmt.Println(sk)
}
```
### <span id="ArrayStack_Push">Push</span>
<p>将元素加入数组栈</p>
<b>函数签名:</b>
```go
func (s *ArrayStack[T]) Push(value T)
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewArrayStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
fmt.Println(sk.Data()) //[]int{3, 2, 1}
}
```
### <span id="ArrayStack_Pop">Pop</span>
<p>删除栈顶元素并返回该元素指针</p>
<b>函数签名:</b>
```go
func (s *ArrayStack[T]) Pop() (*T, error)
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewArrayStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
val, err := sk.Pop()
fmt.Println(err) //nil
fmt.Println(*val) //3
fmt.Println(sk.Data()) //[]int{2, 1}
}
```
### <span id="ArrayStack_Peak">Peak</span>
<p>返回栈顶元素指针</p>
<b>函数签名:</b>
```go
func (s *ArrayStack[T]) Peak() (*T, error)
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewArrayStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
val, err := sk.Peak()
fmt.Println(err) //nil
fmt.Println(*val) //3
fmt.Println(sk.Data()) //[]int{3, 2, 1}
}
```
### <span id="ArrayStack_Data">Data</span>
<p>返回栈中所有元素组成的切片</p>
<b>函数签名:</b>
```go
func (s *ArrayStack[T]) Data() []T
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewArrayStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
fmt.Println(sk.Data()) //[]int{3, 2, 1}
}
```
### <span id="ArrayStack_Size">Size</span>
<p>返回栈中元素的数量</p>
<b>函数签名:</b>
```go
func (s *ArrayStack[T]) Size() int
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewArrayStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
fmt.Println(sk.Size()) //3
}
```
### <span id="ArrayStack_IsEmpty">IsEmpty</span>
<p>判断栈是否为空</p>
<b>函数签名:</b>
```go
func (s *ArrayStack[T]) IsEmpty() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewArrayStack[int]()
fmt.Println(sk.IsEmpty()) //true
sk.Push(1)
sk.Push(2)
sk.Push(3)
fmt.Println(sk.IsEmpty()) //false
}
```
### <span id="ArrayStack_Clear">Clear</span>
<p>清空栈元素,使栈为空</p>
<b>函数签名:</b>
```go
func (s *ArrayStack[T]) Clear()
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewArrayStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
sk.Clear()
fmt.Println(sk.Data()) //[]int{}
}
```
### 2. LinkedStack
链表实现的栈结构。
### <span id="NewLinkedStack">NewLinkedStack</span>
<p>返回LinkedStack指针实例</p>
<b>函数签名:</b>
```go
type StackNode[T any] struct {
Value T
Next *StackNode[T]
}
type LinkedStack[T any] struct {
top *datastructure.StackNode[T]
length int
}
func NewLinkedStack[T any]() *LinkedStack[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewLinkedStack[int]()
fmt.Println(sk)
}
```
### <span id="LinkedStack_Push">Push</span>
<p>将元素加入链表栈</p>
<b>函数签名:</b>
```go
func (s *LinkedStack[T]) Push(value T)
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewLinkedStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
fmt.Println(sk.Data()) //[]int{3, 2, 1}
}
```
### <span id="LinkedStack_Pop">Pop</span>
<p>删除栈顶元素并返回该元素指针</p>
<b>函数签名:</b>
```go
func (s *LinkedStack[T]) Pop() (*T, error)
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewLinkedStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
val, err := sk.Pop()
fmt.Println(err) //nil
fmt.Println(*val) //3
fmt.Println(sk.Data()) //[]int{2, 1}
}
```
### <span id="LinkedStack_Peak">Peak</span>
<p>返回栈顶元素指针</p>
<b>函数签名:</b>
```go
func (s *LinkedStack[T]) Peak() (*T, error)
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewLinkedStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
val, err := sk.Peak()
fmt.Println(err) //nil
fmt.Println(*val) //3
fmt.Println(sk.Data()) //[]int{3, 2, 1}
}
```
### <span id="LinkedStack_Data">Data</span>
<p>返回栈中所有元素组成的切片</p>
<b>函数签名:</b>
```go
func (s *LinkedStack[T]) Data() []T
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewLinkedStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
fmt.Println(sk.Data()) //[]int{3, 2, 1}
}
```
### <span id="LinkedStack_Size">Size</span>
<p>返回栈中元素的数量</p>
<b>函数签名:</b>
```go
func (s *LinkedStack[T]) Size() int
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewLinkedStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
fmt.Println(sk.Size()) //3
}
```
### <span id="LinkedStack_IsEmpty">IsEmpty</span>
<p>判断栈是否为空</p>
<b>函数签名:</b>
```go
func (s *LinkedStack[T]) IsEmpty() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewLinkedStack[int]()
fmt.Println(sk.IsEmpty()) //true
sk.Push(1)
sk.Push(2)
sk.Push(3)
fmt.Println(sk.IsEmpty()) //false
}
```
### <span id="LinkedStack_Clear">Clear</span>
<p>清空栈元素,使栈为空</p>
<b>函数签名:</b>
```go
func (s *LinkedStack[T]) Clear()
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewLinkedStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
sk.Clear()
fmt.Println(sk.Data()) //[]int{}
}
```
### <span id="LinkedStack_Print">Print</span>
<p>打印链表栈结构</p>
<b>函数签名:</b>
```go
func (s *LinkedStack[T]) Print()
```
<b>示例:</b>
```go
package main
import (
"fmt"
stack "github.com/duke-git/lancet/v2/datastructure/stack"
)
func main() {
sk := stack.NewLinkedStack[int]()
sk.Push(1)
sk.Push(2)
sk.Push(3)
sk.Print() //[ &{Value:3 Next:0xc000010260}, &{Value:2 Next:0xc000010250}, &{Value:1 Next:<nil>}, ]
}
```

View File

@@ -0,0 +1,526 @@
# Tree
树是树节点的集合。 每个树节点都有一个值,一个指向左节点的左指针和一个指向右节点的右指针。
<div STYLE="page-break-after: always;"></div>
## 源码
- [https://github.com/duke-git/lancet/blob/main/datastructure/tree/bstree.go](https://github.com/duke-git/lancet/blob/main/datastructure/tree/bstree.go)
<div STYLE="page-break-after: always;"></div>
## 用法
```go
import (
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
### 1. BSTree
- [NewBSTree](#NewBSTree)
- [Insert](#BSTree_Insert)
- [Delete](#BSTree_Delete)
- [PreOrderTraverse](#BSTree_PreOrderTraverse)
- [InOrderTraverse](#BSTree_InOrderTraverse)
- [PostOrderTraverse](#BSTree_PostOrderTraverse)
- [LevelOrderTraverse](#BSTree_LevelOrderTraverse)
- [Depth](#BSTree_Depth)
- [HasSubTree](#BSTree_HasSubTree)
- [Print](#BSTree_Print)
<div STYLE="page-break-after: always;"></div>
## 文档
## 1. BSTree
BSTree是一种二叉搜索树数据结构其中每个节点有两个孩子分别称为左孩子和右孩子。 在 BSTree 中leftNode < rootNode < rightNode。 T类型应该实现lancetconstraints.Comparator。
### <span id="NewBSTree">NewBSTree</span>
<p>返回BSTree指针实例</p>
<b>函数签名:</b>
```go
func NewBSTree[T any](rootData T, comparator lancetconstraints.Comparator) *BSTree[T]
type BSTree[T any] struct {
root *datastructure.TreeNode[T]
comparator lancetconstraints.Comparator
}
type TreeNode[T any] struct {
Value T
Left *TreeNode[T]
Right *TreeNode[T]
}
```
<b>示例:</b>
```go
package main
import (
"fmt"
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
bstree := tree.NewBSTree(6, &intComparator{})
fmt.Println(bstree) //
}
```
### <span id="BSTree_Insert">Insert</span>
<p>将值插入二叉搜索树</p>
<b>函数签名:</b>
```go
func (t *BSTree[T]) Insert(data T)
```
<b>示例:</b>
```go
package main
import (
"fmt"
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
bstree := tree.NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
fmt.Println(bstree.PreOrderTraverse()) //6, 5, 2, 4, 7
}
```
### <span id="BSTree_Delete">Delete</span>
<p>删除插入二叉搜索树中指定的值</p>
<b>函数签名:</b>
```go
func (t *BSTree[T]) Delete(data T)
```
<b>示例:</b>
```go
package main
import (
"fmt"
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
bstree := tree.NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
bstree.Delete(4)
fmt.Println(bstree.PreOrderTraverse()) //2, 5, 6, 7
}
```
### <span id="BSTree_PreOrderTraverse">PreOrderTraverse</span>
<p>按前序遍历树节点</p>
<b>函数签名:</b>
```go
func (t *BSTree[T]) PreOrderTraverse() []T
```
<b>示例:</b>
```go
package main
import (
"fmt"
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
bstree := tree.NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
fmt.Println(bstree.PreOrderTraverse()) //6, 5, 2, 4, 7
}
```
### <span id="BSTree_InOrderTraverse">InOrderTraverse</span>
<p>按中序遍历树节点</p>
<b>函数签名:</b>
```go
func (t *BSTree[T]) InOrderTraverse() []T
```
<b>示例:</b>
```go
package main
import (
"fmt"
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
bstree := tree.NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
fmt.Println(bstree.InOrderTraverse()) //2, 4, 5, 6, 7
}
```
### <span id="BSTree_PostOrderTraverse">PostOrderTraverse</span>
<p>按后序遍历树节点</p>
<b>函数签名:</b>
```go
func (t *BSTree[T]) PostOrderTraverse() []T
```
<b>示例:</b>
```go
package main
import (
"fmt"
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
bstree := tree.NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
fmt.Println(bstree.PostOrderTraverse()) //5, 2, 4, 7, 6
}
```
### <span id="BSTree_LevelOrderTraverse">LevelOrderTraverse</span>
<p>按节点层次遍历树节点</p>
<b>函数签名:</b>
```go
func (t *BSTree[T]) LevelOrderTraverse() []T
```
<b>示例:</b>
```go
package main
import (
"fmt"
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
bstree := tree.NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
fmt.Println(bstree.LevelOrderTraverse()) //6, 5, 7, 2, 4
}
```
### <span id="BSTree_Depth">Depth</span>
<p>获取树的深度</p>
<b>函数签名:</b>
```go
func (t *BSTree[T]) Depth() int
```
<b>示例:</b>
```go
package main
import (
"fmt"
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
bstree := tree.NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
fmt.Println(bstree.Depth()) //4
}
```
### <span id="BSTree_HasSubTree">HasSubTree</span>
<p>判断给定树是否是子树</p>
<b>函数签名:</b>
```go
func (t *BSTree[T]) HasSubTree(subTree *BSTree[T]) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
superTree := tree.NewBSTree(8, &intComparator{})
superTree.Insert(4)
superTree.Insert(5)
superTree.Insert(6)
superTree.Insert(9)
superTree.Insert(4)
subTree := tree.NewBSTree(5, &intComparator{})
subTree.Insert(4)
subTree.Insert(6)
fmt.Println(superTree.HasSubTree(subTree)) //true
fmt.Println(subTree.HasSubTree(superTree)) //false
}
```
### <span id="BSTree_Print">Print</span>
<p>打印树结构</p>
<b>函数签名:</b>
```go
func (t *BSTree[T]) Print()
```
<b>示例:</b>
```go
package main
import (
"fmt"
tree "github.com/duke-git/lancet/v2/datastructure/tree"
)
type intComparator struct{}
func (c *intComparator) Compare(v1, v2 any) int {
val1, _ := v1.(int)
val2, _ := v2.(int)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
}
func main() {
bstree := tree.NewBSTree(6, &intComparator{})
bstree.Insert(7)
bstree.Insert(5)
bstree.Insert(2)
bstree.Insert(4)
fmt.Println(bstree.Print())
// 6
// / \
// / \
// / \
// / \
// 5 7
// /
// /
// 2
// \
// 4
}
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,841 @@
# Fileutil
fileutil 包支持文件基本操作。
<div STYLE="page-break-after: always;"></div>
## 源码:
- [https://github.com/duke-git/lancet/blob/main/fileutil/file.go](https://github.com/duke-git/lancet/blob/main/fileutil/file.go)
<div STYLE="page-break-after: always;"></div>
## 用法:
```go
import (
"github.com/duke-git/lancet/v2/fileutil"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [ClearFile](#ClearFile)
- [CreateFile](#CreateFile)
- [CreateDir](#CreateDir)
- [CopyFile](#CopyFile)
- [CurrentPath](#CurrentPath)
- [FileMode](#FileMode)
- [MiMeType](#MiMeType)
- [IsExist](#IsExist)
- [IsLink](#IsLink)
- [IsDir](#IsDir)
- [ListFileNames](#ListFileNames)
- [RemoveFile](#RemoveFile)
- [ReadFileToString](#ReadFileToString)
- [ReadFileByLine](#ReadFileByLine)
- [Zip](#Zip)
- [ZipAppendEntry](#ZipAppendEntry)
- [UnZip](#UnZip)
- [IsZipFile](#IsZipFile)
- [FileSize](#FileSize)
- [MTime](#MTime)
- [Sha](#Sha)
- [ReadCsvFile](#ReadCsvFile)
- [WriteCsvFile](#WriteCsvFile)
- [WriteStringToFile](#WriteStringToFile)
- [WriteBytesToFile](#WriteBytesToFile)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="ClearFile">ClearFile</span>
<p>清空文件内容</p>
<b>函数签名:</b>
```go
func ClearFile(path string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/NRZ0ZT-G94H)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.ClearFile("./test.txt")
if err != nil {
fmt.Println(err)
}
}
```
### <span id="CreateFile">CreateFile</span>
<p>创建文件创建成功返回true, 否则返回false</p>
<b>函数签名:</b>
```go
func CreateFile(path string) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/lDt8PEsTNKI)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
isCreatedSucceed := fileutil.CreateFile("./test.txt")
fmt.Println(isCreatedSucceed)
}
```
### <span id="CreateDir">CreateDir</span>
<p>使用绝对路径创建嵌套目录,例如/a/, /a/b</p>
<b>函数签名:</b>
```go
func CreateDir(absPath string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/qUuCe1OGQnM)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.CreateDir("/a/b") // will create folder /a/b
fmt.Println(err)
}
```
### <span id="CopyFile">CopyFile</span>
<p>拷贝文件,会覆盖原有的文件</p>
<b>函数签名:</b>
```go
func CopyFile(srcPath string, dstPath string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Jg9AMJMLrJi)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.CopyFile("./test.txt", "./test_copy.txt")
if err != nil {
fmt.Println(err)
}
}
```
### <span id="CurrentPath">CurrentPath</span>
<p>返回当前位置的绝对路径。</p>
<b>函数签名:</b>
```go
func CurrentPath() string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/s74a9iBGcSw)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
absPath := CurrentPath()
fmt.Println(absPath)
}
```
### <span id="FileMode">FileMode</span>
<p>获取文件mode信息</p>
<b>函数签名:</b>
```go
func FileMode(path string) (fs.FileMode, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/2l2hI42fA3p)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
mode, err := fileutil.FileMode("./test.txt")
if err != nil {
fmt.Println(err)
}
fmt.Println(mode)
}
```
### <span id="MiMeType">MiMeType</span>
<p>获取文件mime类型, 'file'参数的类型必须是string或者*os.File</p>
<b>函数签名:</b>
```go
func MiMeType(file any) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/bd5sevSUZNu)</span></b>
```go
package main
import (
"fmt"
"os"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
type1 := fileutil.MiMeType("./test.txt")
fmt.Println(type1) //text/plain; charset=utf-8
f, _ := os.Open("./file.go")
type2 := fileutil.MiMeType(f)
fmt.Println(type2) //text/plain; charset=utf-8
}
```
### <span id="IsExist">IsExist</span>
<p>判断文件或目录是否存在</p>
<b>函数签名:</b>
```go
func IsExist(path string) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/nKKXt8ZQbmh)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
fileutil.CreateFile("./test.txt")
isFileExist := fileutil.IsExist("./test.txt")
fmt.Println(isFileExist) //true
}
```
### <span id="IsLink">IsLink</span>
<p>判断文件是否是符号链接</p>
<b>函数签名:</b>
```go
func IsLink(path string) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/TL-b-Kzvf44)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
isLinkFile := fileutil.IsLink("./test.txt")
fmt.Println(isLinkFile) //false
}
```
### <span id="IsDir">IsDir</span>
<p>判断参数是否是目录</p>
<b>函数签名:</b>
```go
func IsDir(path string) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/WkVwEKqtOWk)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
isDir := fileutil.IsDir("./")
fmt.Println(isDir) //true
isDir = fileutil.IsDir("./test.txt")
fmt.Println(isDir) //false
}
```
### <span id="ListFileNames">ListFileNames</span>
<p>返回目录下所有文件名</p>
<b>函数签名:</b>
```go
func ListFileNames(path string) ([]string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Tjd7Y07rejl)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
fileNames, _ := fileutil.ListFileNames("./")
fmt.Println(fileNames)
}
```
### <span id="RemoveFile">RemoveFile</span>
<p>删除文件</p>
<b>函数签名:</b>
```go
func RemoveFile(path string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/P2y0XW8a1SH)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.RemoveFile("./test.txt")
if err != nil {
fmt.Println(err)
}
}
```
### <span id="ReadFileToString">ReadFileToString</span>
<p>读取文件内容并返回字符串</p>
<b>函数签名:</b>
```go
func ReadFileToString(path string) (string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/cmfwp_5SQTp)</span></b>
```go
package main
import (
"fmt"
"os"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
path := "./test.txt"
fileutil.CreateFile(path)
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
f.WriteString("hello world")
content, _ := fileutil.ReadFileToString(path)
fmt.Println(content) //hello world
}
```
### <span id="ReadFileByLine">ReadFileByLine</span>
<p>按行读取文件内容,返回字符串切片包含每一行</p>
<b>函数签名:</b>
```go
func ReadFileByLine(path string)([]string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/svJP_7ZrBrD)</span></b>
```go
package main
import (
"fmt"
"os"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
path := "./text.txt"
fileutil.CreateFile(path)
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
f.WriteString("hello\nworld")
contents, _ := fileutil.ReadFileByLine(path)
fmt.Println(contents) //[]string{"hello", "world"}
}
```
### <span id="Zip">Zip</span>
<p>zip压缩文件, fpath参数可以是文件或目录</p>
<b>函数签名:</b>
```go
func Zip(fpath string, destPath string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/j-3sWBp8ik_P)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.Zip("./test.txt", "./test.zip")
if err != nil {
fmt.Println(err)
}
}
```
### <span id="ZipAppendEntry">ZipAppendEntry</span>
<p>通过将单个文件或目录追加到现有的zip文件</p>
<b>函数签名:</b>
```go
func ZipAppendEntry(fpath string, destPath string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/cxvaT8TRNQp)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.ZipAppendEntry("./test.txt", "./test.zip")
if err != nil {
fmt.Println(err)
}
}
```
### <span id="UnZip">UnZip</span>
<p>zip解压缩文件并保存在目录中</p>
<b>函数签名:</b>
```go
func UnZip(zipFile string, destPath string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/g0w34kS7B8m)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.Zip("./test.zip", "./unzip/test.txt")
if err != nil {
fmt.Println(err)
}
}
```
### <span id="IsZipFile">IsZipFile</span>
<p>判断文件是否是zip压缩文件。</p>
<b>函数签名:</b>
```go
func IsZipFile(filepath string) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9M0g2j_uF_e)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
isZip := fileutil.IsZipFile("./zipfile.zip")
fmt.Println(isZip)
}
```
### <span id="FileSize">FileSize</span>
<p>返回文件字节大小。</p>
<b>函数签名:</b>
```go
func FileSize(path string) (int64, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/H9Z05uD-Jjc)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
size, err := fileutil.FileSize("./testdata/test.txt")
fmt.Println(size)
fmt.Println(err)
// Output:
// 20
// <nil>
}
```
### <span id="MTime">MTime</span>
<p>返回文件修改时间(unix timestamp).</p>
<b>函数签名:</b>
```go
func MTime(filepath string) (int64, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/s_Tl7lZoAaY)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
mtime, err := fileutil.MTime("./testdata/test.txt")
fmt.Println(mtime)
fmt.Println(err)
// Output:
// 1682391110
// <nil>
}
```
### <span id="Sha">Sha</span>
<p>返回文件sha值参数`shaType` 应传值为: 1, 256512.</p>
<b>函数签名:</b>
```go
func Sha(filepath string, shaType ...int) (string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/VfEEcO2MJYf)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
sha1, err := fileutil.Sha("./testdata/test.txt", 1)
sha256, _ := fileutil.Sha("./testdata/test.txt", 256)
sha512, _ := fileutil.Sha("./testdata/test.txt", 512)
fmt.Println(sha1)
fmt.Println(sha256)
fmt.Println(sha512)
fmt.Println(err)
// Output:
// dda3cf10c5a6ff6c6659a497bf7261b287af2bc7
// aa6d0a3fbc3442c228d606da09e0c1dc98c69a1cac3da1909199e0266171df35
// d22aba2a1b7a2e2f512756255cc1c3708905646920cb1eb95e45b531ba74774dbbb89baebf1f716220eb9cf4908f1cfc5b2a01267704d9a59f59d77cab609870
// <nil>
}
```
### <span id="ReadCsvFile">ReadCsvFile</span>
<p>读取csv文件内容到切片</p>
<b>函数签名:</b>
```go
func ReadCsvFile(filepath string) ([][]string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/OExTkhGEd3_u)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
content, err := fileutil.ReadCsvFile("./testdata/test.csv")
fmt.Println(content)
fmt.Println(err)
// Output:
// [[Bob 12 male] [Duke 14 male] [Lucy 16 female]]
// <nil>
}
```
### <span id="WriteCsvFile">WriteCsvFile</span>
<p>向csv文件写入内容。</p>
<b>函数签名:</b>
```go
func WriteCsvFile(filepath string, records [][]string, append bool) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dAXm58Q5U1o)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
fpath := "./test.csv"
fileutil.CreateFile(fpath)
f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
data := [][]string{
{"Lili", "22", "female"},
{"Jim", "21", "male"},
}
err := fileutil.WriteCsvFile(fpath, data, false)
if err != nil {
return
}
content, err := fileutil.ReadCsvFile(fpath)
if err != nil {
return
}
fmt.Println(content)
// Output:
// [[Lili 22 female] [Jim 21 male]]
}
```
### <span id="WriteBytesToFile">WriteBytesToFile</span>
<p>将bytes写入文件。</p>
<b>函数签名:</b>
```go
func WriteBytesToFile(filepath string, content []byte) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/s7QlDxMj3P8)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
filepath := "./bytes.txt"
file, err := os.Create(filepath)
if err != nil {
return
}
defer file.Close()
err = fileutil.WriteBytesToFile(filepath, []byte("hello"))
if err != nil {
return
}
content, err := fileutil.ReadFileToString(filepath)
if err != nil {
return
}
os.Remove(filepath)
fmt.Println(content)
// Output:
// hello
}
```
### <span id="WriteStringToFile">WriteStringToFile</span>
<p>将字符串写入文件。</p>
<b>函数签名:</b>
```go
func WriteStringToFile(filepath string, content string, append bool) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/GhLS6d8lH_g)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
filepath := "./test.txt"
file, err := os.Create(filepath)
if err != nil {
return
}
defer file.Close()
err = fileutil.WriteStringToFile(filepath, "hello", true)
if err != nil {
return
}
content, err := fileutil.ReadFileToString(filepath)
if err != nil {
return
}
os.Remove(filepath)
fmt.Println(content)
// Output:
// hello
}
```

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