├── .gitignore ├── LICENSE ├── README.md ├── README_zh.md ├── collectors ├── collectors.go └── static.go ├── go.mod ├── stream ├── extracted.go ├── stream.go └── stream_test.go └── utils ├── common.go └── conv.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | 10 | .idea 11 | .DS_Store 12 | blog/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | **Read this in other languages: [【English](README.md) | [中文】](README_zh.md).** 3 | 4 | **Quick start:** 5 | ``` 6 | go get github.com/todocoder/go-stream 7 | ``` 8 | 9 | # Stream Collections for Go. Inspired in Java 8 Streams and go-zero. 10 | 11 | ## Introduction 12 |   In JAVA, when it comes to operating elements in collection classes such as arrays and Collections, they are usually processed one by one through a loop, or processed using a Stream. Well, slices are mostly used in Go, so the Java-based stream operations here are customary to use the Go language ( 13 | 1.18+)'s generics and channels implement some simple stream operation functions. 14 | 15 | **Data converted with Go-Stream or Groupby can be used directly without assertions.** 16 | 17 | > go-stream code address:https://github.com/todocoder/go-stream 18 | 19 | ## Stream Introduction 20 | 21 |   Stream operations can be divided into three types: Stream generation, Stream intermediate processing, and Stream termination. 22 | 23 | ### Stream generation 24 | 25 |   Mainly responsible for creating a new Stream or creating a new Stream based on an existing array. 26 | 27 | | API | Function Description | 28 | |------------------|------------------------------------------------------------------------------------------------------------------------| 29 | | Of() | Create a new stream serial stream object through variable parameters `(values ...T)` | 30 | | OfParallel() | Create a stream serial stream object that can be executed in parallel through variable parameters `(values ...T)` | 31 | | OfFrom() | Create a new stream serial stream object through the method `(generate func(source chan<- T))` | 32 | | OfFromParallel() | Generate a serial stream object that can be executed in parallel through the method `(generate func(source chan<- T))` | 33 | | Concat() | Multiple streams are spliced together to create a serial execution stream serial stream object. | 34 | 35 | ### Stream intermediate processing 36 | 37 |   Mainly responsible for processing Stream and returning a new Stream object. Intermediate processing operations can be superimposed. 38 | 39 | | API | Function Description | 40 | |------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 41 | | Filter() | Filter elements that meet the requirements according to conditions and return a new stream | 42 | | Map() | Convert existing elements to another object type according to conditions, one-to-one logic, and return a new type of stream | 43 | | FlatMap() | Convert existing elements to another object type according to conditions, one-to-many logic, that is, the original element object may be converted into one or more elements of a new type, and a new stream is returned. | 44 | | Skip() | Skip the specified number of elements in front of the current stream | 45 | | Limit() | Only retain the specified number of elements in front of the current stream and return a new stream | 46 | | Concat() | Multiple streams are spliced under the current stream | 47 | | Distinct() | Eliminate duplicate elements that meet the requirements according to conditions and return a new stream. | 48 | | Sorted() | Sort elements according to conditions and return a new stream | 49 | | Reverse() | Reverse elements in a stream | 50 | | Peek() | Traverse each element in the stream one by one and return the processed stream | 51 | 52 | ### Stream termination 53 | 54 |   After the termination function operation, the Stream will end, and finally some logical processing may be performed, or some execution result data may be returned as required. 55 | 56 | | API | Function Description | 57 | |-------------|------------------------------------------------------------------------------------------| 58 | | FindFirst() | Get the first element | 59 | | FindLast() | Get the last element | 60 | | ForEach() | Traverse the elements one by one and then execute the given processing logic | 61 | | Reduce() | Aggregate elements in a stream | 62 | | AnyMatch() | Returns whether there is an element in this stream that satisfies the provided condition | 63 | | AllMatch() | Returns whether all conditions in this stream are met | 64 | | NoneMatch() | Returns whether all conditions in this stream are not met | 65 | | Count() | Returns the number of elements in this stream | 66 | | Max() | Returns the maximum value of the element after stream processing | 67 | | Min() | Returns the minimum value of the element after stream processing | 68 | | ToSlice() | Convert streams into slices after processing | 69 | | Collect() | Convert the stream to the specified type, specified through collectors.Collector | 70 | 71 | ### Conversion Function 72 | 73 |   Through these functions you can implement type conversion, grouping, flatmap and other processing 74 | 75 | > Note: These **functions** are very useful and the most commonly used. Due to the limitations of Go language generics, **Go language methods** do not support their own independent generics, so the method in Stream is used for conversion. It can only be replaced by interface{}. This will have a very troublesome problem. It must be forced to be used after conversion, so I wrote these as conversion functions so that they will not be subject to the generics of the class (struct).。 76 | 77 | | API | Function Description | 78 | |--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 79 | | Map() | Type conversion (advantage: unlike the Map above, it can be used directly after conversion without forced conversion) | 80 | | FlatMap() | Convert existing elements to another object type according to conditions, one-to-many logic, that is, an original element object may be converted into one or more elements of a new type, and a new stream is returned (advantage: same as Map) | 81 | | GroupingBy() | Traverse the elements one by one and then execute the given processing logic | 82 | | Collect() | Convert the stream to the specified type and specify it through collectors.Collector (advantage: the converted type can be used directly without forced conversion) | 83 | 84 | ## Use of Go-Stream 85 | 86 | ### Introduce 87 | 88 |   Due to the use of generics, the supported version is golang 1.18+ 89 | **Quick start:** 90 | ``` 91 | go get github.com/todocoder/go-stream 92 | ``` 93 | 94 | ### Use of ForEach and Peek 95 | 96 |   Both ForEach and Peek can be used to traverse elements and process them one by one. 97 | But Peek is an intermediate method, while ForEach is a termination method. In other words, Peek can only process elements in the middle of the stream, and cannot directly execute it to obtain the result. It will only be executed when there are other termination operations; and ForEach, as a termination method without a return value, can directly execute the relevant operate. 98 | 99 | #### ForEach 100 | 101 | ```go 102 | package todocoder 103 | 104 | type TestItem struct { 105 | itemNum int 106 | itemValue string 107 | } 108 | 109 | func TestForEachAndPeek(t *testing.T) { 110 | // ForEach 111 | stream.Of( 112 | TestItem{itemNum: 1, itemValue: "item1"}, 113 | TestItem{itemNum: 2, itemValue: "item2"}, 114 | TestItem{itemNum: 3, itemValue: "item3"}, 115 | ).ForEach(func(item TestItem) { 116 | fmt.Println(item.itemValue) 117 | }) 118 | // Peek 119 | stream.Of( 120 | TestItem{itemNum: 1, itemValue: "item1"}, 121 | TestItem{itemNum: 2, itemValue: "item2"}, 122 | TestItem{itemNum: 3, itemValue: "item3"}, 123 | ).Peek(func(item *TestItem) { 124 | item.itemValue = item.itemValue + "peek" 125 | }).ForEach(func(item TestItem) { 126 | fmt.Println(item.itemValue) 127 | }) 128 | } 129 | ``` 130 | 131 | The result is as follows: 132 | 133 | ``` 134 | item1 135 | item2 136 | item3 137 | item1peek 138 | item2peek 139 | item3peek 140 | ``` 141 | 142 | From the code and results, we know that ForEach is only used to loop through the elements in the stream. Peek can modify elements in the stream in the middle of the stream. 143 | 144 | ### Filter、Sorted、Distinct、Skip、Limit、Reverse 145 | 146 |   These are the more commonly used intermediate processing methods in go-stream, and the specific instructions are marked above. If used, we can use one or more combinations in the stream. 147 | 148 | ```go 149 | package todocoder 150 | 151 | func TestStream(t *testing.T) { 152 | // ForEach 153 | res := stream.Of( 154 | TestItem{itemNum: 7, itemValue: "item7"}, 155 | TestItem{itemNum: 6, itemValue: "item6"}, 156 | TestItem{itemNum: 1, itemValue: "item1"}, 157 | TestItem{itemNum: 2, itemValue: "item2"}, 158 | TestItem{itemNum: 3, itemValue: "item3"}, 159 | TestItem{itemNum: 4, itemValue: "item4"}, 160 | TestItem{itemNum: 5, itemValue: "item5"}, 161 | TestItem{itemNum: 5, itemValue: "item5"}, 162 | TestItem{itemNum: 5, itemValue: "item5"}, 163 | TestItem{itemNum: 8, itemValue: "item8"}, 164 | TestItem{itemNum: 9, itemValue: "item9"}, 165 | ).Filter(func(item TestItem) bool { 166 | // Filter out values of 4 167 | return item.itemNum != 4 168 | }).Distinct(func(item TestItem) any { 169 | // Press itemNum to remove duplicates 170 | return item.itemNum 171 | }).Sorted(func(a, b TestItem) bool { 172 | // Sort by itemNum in ascending order 173 | return a.itemNum < b.itemNum 174 | }).Skip(1).Limit(6).Reverse().Collect(collectors.ToSlice[TestItem]()) 175 | fmt.Println(res) 176 | } 177 | ``` 178 | 179 | 1. Use Filter() to filter out the value of 4 180 | 2. Deduplicate itemNum through Distinct() (based on the first step, the following is the same as the previous step) 181 | 3. Sorted() by itemNum in ascending order by Sorted 182 | 4. Use Skip() to start from the element with index 1 183 | 5. Use Limit() to intercept the top 6 elements 184 | 6. Use Reverse() to reverse elements in a stream 185 | 7. Use Collect() to terminate the operation and collect the final processed data into Slice 186 | 187 | result: 188 | 189 | ``` 190 | [{8 item8} {7 item7} {6 item6} {5 item5} {3 item3} {2 item2}] 191 | ``` 192 | 193 | ### AllMatch、AnyMatch、NoneMatch、Count、FindFirst、FindLast 194 | 195 |   These methods all belong to the simple result termination methods mentioned here. code show as below: 196 | 197 | ```go 198 | package todocoder 199 | 200 | func TestSimple(t *testing.T) { 201 | allMatch := stream.Of( 202 | TestItem{itemNum: 7, itemValue: "item7"}, 203 | TestItem{itemNum: 6, itemValue: "item6"}, 204 | TestItem{itemNum: 8, itemValue: "item8"}, 205 | TestItem{itemNum: 1, itemValue: "item1"}, 206 | ).AllMatch(func(item TestItem) bool { 207 | // Returns whether all in this stream == 1 208 | return item.itemNum == 1 209 | }) 210 | fmt.Println(allMatch) 211 | 212 | anyMatch := stream.Of( 213 | TestItem{itemNum: 7, itemValue: "item7"}, 214 | TestItem{itemNum: 6, itemValue: "item6"}, 215 | TestItem{itemNum: 8, itemValue: "item8"}, 216 | TestItem{itemNum: 1, itemValue: "item1"}, 217 | ).Filter(func(item TestItem) bool { 218 | return item.itemNum != 1 219 | }).AnyMatch(func(item TestItem) bool { 220 | // Returns whether there is == 8 in this stream 221 | return item.itemNum == 8 222 | }) 223 | fmt.Println(anyMatch) 224 | 225 | noneMatch := stream.Of( 226 | TestItem{itemNum: 7, itemValue: "item7"}, 227 | TestItem{itemNum: 6, itemValue: "item6"}, 228 | TestItem{itemNum: 8, itemValue: "item8"}, 229 | TestItem{itemNum: 1, itemValue: "item1"}, 230 | ).Filter(func(item TestItem) bool { 231 | return item.itemNum != 1 232 | }).NoneMatch(func(item TestItem) bool { 233 | // Returns whether all in this stream are not equal to 8 234 | return item.itemNum == 8 235 | }) 236 | fmt.Println(noneMatch) 237 | 238 | resFirst := stream.Of( 239 | TestItem{itemNum: 1, itemValue: "item1"}, 240 | TestItem{itemNum: 2, itemValue: "item2"}, 241 | TestItem{itemNum: 3, itemValue: "item3"}, 242 | ).FindFirst() 243 | fmt.Println(resFirst.Get()) 244 | 245 | resLast := stream.Of( 246 | TestItem{itemNum: 1, itemValue: "item1"}, 247 | TestItem{itemNum: 2, itemValue: "item2"}, 248 | TestItem{itemNum: 3, itemValue: "item3"}, 249 | ).FindLast() 250 | fmt.Println(resLast.Get()) 251 | } 252 | ``` 253 | 254 | result: 255 | 256 | ``` 257 | false 258 | true 259 | false 260 | {1 item1} true 261 | {3 item3} true 262 | ``` 263 | 264 | ### Map、FlatMap 265 | 266 | Both Map and FlatMap are used to convert existing elements into other elements. The difference is: 267 | 268 | 1. Map() converts existing elements into another object type according to conditions, one-to-one logic 269 | 2. FlatMap() converts existing elements to another object type according to conditions, one-to-many logic 270 | 271 | For example, I want to convert int 1 to TestItem{itemNum: 1, itemValue: "item1"} 272 | 273 | ```go 274 | package todocoder 275 | 276 | func TestMap(t *testing.T) { 277 | res := stream.Of([]int{1, 2, 3, 4, 7}...).Map(func(item int) any { 278 | return TestItem{ 279 | itemNum: item, 280 | itemValue: fmt.Sprintf("item%d", item), 281 | } 282 | }).Collect(collectors.ToSlice[any]()) 283 | fmt.Println(res) 284 | } 285 | ``` 286 | 287 | ``` 288 | [{1 item1} {2 item2} {3 item3} {4 item4} {7 item7}] 289 | ``` 290 | 291 | So if I want to convert two strings ["wo shi todocoder", "ha ha ha"] into ["wo", "shi", "todocoder", "ha", "ha", "ha"] 292 | Using Map won’t work, so you need to use FlatMap 293 | 294 | ```go 295 | package todocoder 296 | 297 | func TestFlatMap(t *testing.T) { 298 | // Convert two strings ["wo shi todocoder", "ha ha ha"] into ["wo", "shi", "todocoder", "ha", "ha", "ha"] 299 | res := stream.Of([]string{"wo shi todocoder", "ha ha ha"}...).FlatMap(func(s string) stream.Stream[any] { 300 | return stream.OfFrom(func(source chan<- any) { 301 | for _, str := range strings.Split(s, " ") { 302 | source <- str 303 | } 304 | }) 305 | }).Collect(collectors.ToSlice[any]()) 306 | fmt.Println(res) 307 | } 308 | ``` 309 | 310 | ``` 311 | [wo shi todocoder ha ha ha] 312 | ``` 313 | 314 | > **Note: It is necessary to add here that as long as it is processed by Map or FlatMap, the type will become `any` instead of generic `T`. If forced type processing is required, manual conversion is required. 315 | > This reason is caused by the limitations of Go generics. Other types of generics cannot be defined in the struct method. This will depend on whether it will be officially supported in the future.** 316 | > 317 | > You can see the following code: 318 | 319 | ```go 320 | package todocoder 321 | 322 | func TestMap(t *testing.T) { 323 | res := stream.Of( 324 | TestItem{itemNum: 3, itemValue: "item3"}, 325 | ).FlatMap(func(item TestItem) stream.Stream[any] { 326 | return Of[any]( 327 | TestItem{itemNum: item.itemNum * 10, itemValue: fmt.Sprintf("%s+%d", item.itemValue, item.itemNum)}, 328 | TestItem{itemNum: item.itemNum * 20, itemValue: fmt.Sprintf("%s+%d", item.itemValue, item.itemNum)}, 329 | ) 330 | }).Map(func(item any) any { 331 | // Type assertion is required here 332 | ite := item.(TestItem) 333 | return ToTestItem{ 334 | itemNum: ite.itemNum, 335 | itemValue: ite.itemValue, 336 | } 337 | }).Collect(collectors.ToSlice[any]()) 338 | fmt.Println(res) 339 | } 340 | ``` 341 | 342 | ### collectors.ToMap、collectors.GroupBy 343 | 344 |   These two are relatively complex termination methods. `ToMap` is similar to `Collectors.toMap()` in Java stream, which can convert the slice array into a slice map, `GroupBy` 345 | Similar to the `Collectors.groupingby()` method in Java stream, grouped by a certain dimension 346 | 347 | The following slice list: 348 | 349 | *TestItem{itemNum: 1, itemValue: "item1"},* 350 | *TestItem{itemNum: 2, itemValue: "item2"},* 351 | *TestItem{itemNum: 2, itemValue: "item3"}* 352 | 353 | 1. The first requirement is: convert this list into a Map according to itemNum as Key and itemValue as value. 354 | 2. The second requirement is: group this list according to itemNum as Key, group it and convert it into a Map 355 | 356 | Let's take a look at the code: 357 | 358 | ```go 359 | package todocoder 360 | 361 | func TestToMap(t *testing.T) { 362 | // The first requirement 363 | resMap := Of( 364 | TestItem{itemNum: 1, itemValue: "item1"}, 365 | TestItem{itemNum: 2, itemValue: "item2"}, 366 | TestItem{itemNum: 2, itemValue: "item3"}, 367 | ).Collect(collectors.ToMap[TestItem](func(t TestItem) any { 368 | return t.itemNum 369 | }, func(item TestItem) any { 370 | return item.itemValue 371 | }, func(oldV, newV any) any { 372 | return oldV 373 | })) 374 | fmt.Println("The first requirement:") 375 | fmt.Println(resMap) 376 | // The second requirement 377 | resGroup := Of( 378 | TestItem{itemNum: 1, itemValue: "item1"}, 379 | TestItem{itemNum: 2, itemValue: "item2"}, 380 | TestItem{itemNum: 2, itemValue: "item3"}, 381 | ).Collect(collectors.GroupingBy(func(t TestItem) any { 382 | return t.itemNum 383 | }, func(t TestItem) any { 384 | return t 385 | })) 386 | fmt.Println("The second requirement:") 387 | fmt.Println(resGroup) 388 | } 389 | ``` 390 | 391 | ``` 392 | The first requirement: 393 | map[1:item1 2:item2] 394 | The second requirement: 395 | map[1:[{1 item1}] 2:[{2 item2} {2 item3}]] 396 | ``` 397 | 398 | ### Conversion Function 399 | 400 | ### Map、FlatMap(No assertion required) 401 | 402 | Both Map and FlatMap are used to convert existing elements into other elements. The difference is: 403 | 404 | 1. Map() converts existing elements into another object type according to conditions, one-to-one logic 405 | 2. FlatMap() converts existing elements to another object type according to conditions, one-to-many logic 406 | 407 | For example, if I want to convert TestItem{itemNum: 1, itemValue: "item1"} to ToTestItem{itemNum: 1, itemValue: "item1"}, and expand one element into two elements according to certain rules, I can use the following code to fulfill 408 | ```go 409 | func TestFlatMap(t *testing.T) { 410 | res := stream.Map(stream.Of( 411 | TestItem{itemNum: 1, itemValue: "item1"}, 412 | TestItem{itemNum: 2, itemValue: "item2"}, 413 | TestItem{itemNum: 3, itemValue: "item3"}, 414 | ).FlatMap(func(item TestItem) Stream[TestItem] { 415 | return Of[TestItem]( 416 | TestItem{itemNum: item.itemNum * 10, itemValue: fmt.Sprintf("%s+%d", item.itemValue, item.itemNum)}, 417 | TestItem{itemNum: item.itemNum * 20, itemValue: fmt.Sprintf("%s+%d", item.itemValue, item.itemNum)}, 418 | ) 419 | }), func(item TestItem) ToTestItem { 420 | return ToTestItem{ 421 | itemNum: item.itemNum, 422 | itemValue: item.itemValue, 423 | } 424 | }).ToSlice() 425 | fmt.Println(res) 426 | } 427 | ``` 428 | 429 | #### GroupingBy() (Use results without assertions) 430 | Need: 431 | The class has a set of student numbers `{1,2,3,....,12}`, and the information corresponding to 12 people is stored in the memory. Convert this student number into a specific Student class, filter out those with a Score of 1, and Group by Score and sort each group in descending order by Age 432 | 433 | ```go 434 | 435 | studentMap := map[int]Student{ 436 | 1: {Num: 1, Name: "小明", Score: 3, Age: 26}, 437 | 2: {Num: 2, Name: "小红", Score: 4, Age: 27}, 438 | 3: {Num: 3, Name: "小李", Score: 5, Age: 19}, 439 | 4: {Num: 4, Name: "老王", Score: 1, Age: 23}, 440 | 5: {Num: 5, Name: "小王", Score: 2, Age: 29}, 441 | 6: {Num: 6, Name: "小绿", Score: 2, Age: 24}, 442 | 7: {Num: 7, Name: "小蓝", Score: 3, Age: 29}, 443 | 8: {Num: 8, Name: "小橙", Score: 3, Age: 30}, 444 | 9: {Num: 9, Name: "小黄", Score: 4, Age: 22}, 445 | 10: {Num: 10, Name: "小黑", Score: 5, Age: 21}, 446 | 11: {Num: 11, Name: "小紫", Score: 3, Age: 32}, 447 | 12: {Num: 12, Name: "小刘", Score: 2, Age: 35}, 448 | } 449 | 450 | res := GroupingBy(Map(Of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), func(n int) Student { 451 | // Note that the return type here is the target type, no assertion is needed 452 | return studentMap[n] 453 | }).Filter(func(s Student) bool { 454 | // There is no need to convert types when filtering here. 455 | return s.Score != 1 456 | }), func(t Student) int { 457 | return t.Score 458 | }, func(t Student) Student { 459 | return t 460 | }, func(t1 []Student) { 461 | // Sort by age in descending order 462 | sort.Slice(t1, func(i, j int) bool { 463 | return t1[i].Age > t1[j].Age 464 | }) 465 | }) 466 | println(res) 467 | ``` 468 | 469 | 470 | ## At Last 471 | 472 |   As a Java developer, I am used to Stream operations, but I haven’t found a suitable lightweight stream framework. I don’t know whether the official one will be released in the future. Before that, I will simply implement one by myself. I will encounter complex processing processes later. Continuously update to the above 473 | In addition to the above functions, there is also parallel stream processing. If you are interested, you can check the experience by yourself [Test category: stream_test](https://github.com/todocoder/go-stream/blob/master/stream/stream_test.go) 474 | 475 | If you have any questions, please leave a message and we will reply as soon as possible after seeing it. 476 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | 2 | **阅读其他语言版本: [【English](README.md) | [中文】](README_zh.md).** 3 | 4 | **快速使用** 5 | 6 | ``` 7 | go get github.com/todocoder/go-stream 8 | ``` 9 | 10 | # Go-Stream 受到 Java 8 Streams 和 go-zero stream的启发 11 | 12 | ## 简介 13 |   在JAVA中,涉及到对数组、Collection等集合类中的元素进行操作的时候,通常会通过循环的方式进行逐个处理,或者使用Stream的方式进行处理。那么在Go中用的多的是切片,那么这里基于Java的stream的操作习惯用Go语言( 14 | 1.18+)的泛型和通道实现了一些简单的流操作功能。 15 | 16 | **用Go-Stream 转换或者Groupby后的数据 ,可以直接使用,无需断言。** 17 | 18 | > go-stream代码地址:https://github.com/todocoder/go-stream 19 | 20 | ## Stream 介绍 21 | 22 |   可以将Stream流操作分为3种类型:Stream的生成,Stream中间处理,Stream的终止 23 | 24 | ### Stream的生成 25 | 26 |   主要负责新建一个Stream流,或者基于现有的数组创建出新的Stream流。 27 | 28 | | API | 功能说明 | 29 | |------------------|---------------------------------------------------------------| 30 | | Of() | 通过可变参数`(values ...T)`创建出一个新的stream串行流对象 | 31 | | OfParallel() | 通过可变参数`(values ...T)`创建出一个可并行执行stream串行流对象 | 32 | | OfFrom() | 通过方法生成`(generate func(source chan<- T))`创建出一个新的stream串行流对象 | 33 | | OfFromParallel() | 通过方法生成`(generate func(source chan<- T))`创建出一个可并行执行stream串行流对象 | 34 | | Concat() | 多个流拼接的方式创建出一个串行执行stream串行流对象 | 35 | 36 | ### Stream中间处理 37 | 38 |   主要负责对Stream进行处理操作,并返回一个新的Stream对象,中间处理操作可以进行叠加。 39 | 40 | | API | 功能说明 | 41 | |------------|-------------------------------------------------------------------| 42 | | Filter() | 按照条件过滤符合要求的元素, 返回新的stream流 | 43 | | Map() | 按照条件将已有元素转换为另一个对象类型,一对一逻辑,返回新类型的stream流 | 44 | | FlatMap() | 按照条件将已有元素转换为另一个对象类型,一对多逻辑,即原来一个元素对象可能会转换为1个或者多个新类型的元素,返回新的stream流 | 45 | | Skip() | 跳过当前流前面指定个数的元素 | 46 | | Limit() | 仅保留当前流前面指定个数的元素,返回新的stream流 | 47 | | Concat() | 多个流拼接到当前流下 | 48 | | Distinct() | 按照条件去重符合要求的元素, 返回新的stream流 | 49 | | Sorted() | 按照条件对元素进行排序, 返回新的stream流 | 50 | | Reverse() | 对流中元素进行返转操作 | 51 | | Peek() | 对stream流中的每个元素进行逐个遍历处理,返回处理后的stream流 | 52 | 53 | ### Stream的终止 54 | 55 |   通过终止函数操作之后,Stream流将会结束,最后可能会执行某些逻辑处理,或者是按照要求返回某些执行后的结果数据。 56 | 57 | | API | 功能说明 | 58 | |-------------|---------------------------------------| 59 | | FindFirst() | 获取第一个元素 | 60 | | FindLast() | 获取最后一个元素 | 61 | | ForEach() | 对元素进行逐个遍历,然后执行给定的处理逻辑 | 62 | | Reduce() | 对流中元素进行聚合处理 | 63 | | AnyMatch() | 返回此流中是否存在元素满足所提供的条件 | 64 | | AllMatch() | 返回此流中是否全都满足条件 | 65 | | NoneMatch() | 返回此流中是否全都不满足条件 | 66 | | Count() | 返回此流中元素的个数 | 67 | | Max() | 返回stream处理后的元素最大值 | 68 | | Min() | 返回stream处理后的元素最小值 | 69 | | ToSlice() | 将流处理后转化为切片 | 70 | | Collect() | 将流转换为指定的类型,通过collectors.Collector进行指定 | 71 | 72 | ### 转换函数 73 | 74 |    通过这几个函数你可以实现类型转换,分组,flatmap 等处理 75 | 76 | > 注意:这几个**函数**非常有用,也是最常用的,由于Go语言泛型的局限性,**Go语言方法**不支持自己独立的泛型,所以导致用Stream中的方法转换只能用 interface{} 代替,这样会有个非常麻烦的问题就是,转换后用的时候必须得强转才能用,所以我把这些写成转换函数,就不会受制于类(struct) 的泛型了。 77 | 78 | | API | 功能说明 | 79 | | ------------ | ------------------------------------------------------------ | 80 | | Map() | 类型转换(优点:和上面的Map不一样的是,这里转换后可以直接使用,不需要强转) | 81 | | FlatMap() | 按照条件将已有元素转换为另一个对象类型,一对多逻辑,即原来一个元素对象可能会转换为1个或者多个新类型的元素,返回新的stream流(优点:同Map) | 82 | | GroupingBy() | 对元素进行逐个遍历,然后执行给定的处理逻辑 | 83 | | Collect() | 将流转换为指定的类型,通过collectors.Collector进行指定(优点:转换后的类型可以直接使用,无需强转) | 84 | 85 | ## go-stream的使用 86 | 87 | ### 库的引入 88 | 89 |   由于用到了泛型,支持的版本为golang 1.18+ 90 | **快速使用** 91 | 92 | ``` 93 | go get github.com/todocoder/go-stream 94 | ``` 95 | 96 | ### ForEach、Peek的使用 97 | 98 |   ForEach和Peek都可以用于对元素进行遍历然后逐个的进行处理。 99 | 但Peek属于中间方法,而ForEach属于终止方法。也就是说Peek只能在流中间处理元素,没法直接执行得到结果,其后面必须还要有其它终止操作的时候才会被执行;而ForEach作为无返回值的终止方法,则可以直接执行相关操作。 100 | 101 | #### ForEach 102 | 103 | ```go 104 | package todocoder 105 | 106 | type TestItem struct { 107 | itemNum int 108 | itemValue string 109 | } 110 | 111 | func TestForEachAndPeek(t *testing.T) { 112 | // ForEach 113 | stream.Of( 114 | TestItem{itemNum: 1, itemValue: "item1"}, 115 | TestItem{itemNum: 2, itemValue: "item2"}, 116 | TestItem{itemNum: 3, itemValue: "item3"}, 117 | ).ForEach(func(item TestItem) { 118 | fmt.Println(item.itemValue) 119 | }) 120 | // Peek 121 | stream.Of( 122 | TestItem{itemNum: 1, itemValue: "item1"}, 123 | TestItem{itemNum: 2, itemValue: "item2"}, 124 | TestItem{itemNum: 3, itemValue: "item3"}, 125 | ).Peek(func(item *TestItem) { 126 | item.itemValue = item.itemValue + "peek" 127 | }).ForEach(func(item TestItem) { 128 | fmt.Println(item.itemValue) 129 | }) 130 | } 131 | ``` 132 | 133 | 结果如下: 134 | 135 | ``` 136 | item1 137 | item2 138 | item3 139 | item1peek 140 | item2peek 141 | item3peek 142 | ``` 143 | 144 | 从代码及结果中得知,ForEach只是用来循环流中的元素。而Peek可以在流中间修改流中的元素。 145 | 146 | ### Filter、Sorted、Distinct、Skip、Limit、Reverse 147 | 148 |   这几个是go-stream中比较常用的中间处理方法,具体说明在上面已标出。使用的话我们可以在流中一个或多个的组合便用。 149 | 150 | ```go 151 | package todocoder 152 | 153 | func TestStream(t *testing.T) { 154 | // ForEach 155 | res := stream.Of( 156 | TestItem{itemNum: 7, itemValue: "item7"}, 157 | TestItem{itemNum: 6, itemValue: "item6"}, 158 | TestItem{itemNum: 1, itemValue: "item1"}, 159 | TestItem{itemNum: 2, itemValue: "item2"}, 160 | TestItem{itemNum: 3, itemValue: "item3"}, 161 | TestItem{itemNum: 4, itemValue: "item4"}, 162 | TestItem{itemNum: 5, itemValue: "item5"}, 163 | TestItem{itemNum: 5, itemValue: "item5"}, 164 | TestItem{itemNum: 5, itemValue: "item5"}, 165 | TestItem{itemNum: 8, itemValue: "item8"}, 166 | TestItem{itemNum: 9, itemValue: "item9"}, 167 | ).Filter(func(item TestItem) bool { 168 | // 过滤掉1的值 169 | return item.itemNum != 4 170 | }).Distinct(func(item TestItem) any { 171 | // 按itemNum 去重 172 | return item.itemNum 173 | }).Sorted(func(a, b TestItem) bool { 174 | // 按itemNum升序排序 175 | return a.itemNum < b.itemNum 176 | }).Skip(1).Limit(6).Reverse().Collect(collectors.ToSlice[TestItem]()) 177 | fmt.Println(res) 178 | } 179 | ``` 180 | 181 | 1. 使用Filter过滤掉1的值 182 | 2. 通过Distinct对itemNum 去重(在第1步的基础上,下面同理在上一步的基础上) 183 | 3. 通过Sorted 按itemNum升序排序 184 | 4. 用Skip 从下标为1的元素开始 185 | 5. 使用Limit截取排在前6位的元素 186 | 6. 使用Reverse 对流中元素进行返转操作 187 | 7. 使用collect终止操作将最终处理后的数据收集到Slice中 188 | 189 | 结果: 190 | 191 | ``` 192 | [{8 item8} {7 item7} {6 item6} {5 item5} {3 item3} {2 item2}] 193 | ``` 194 | 195 | ### AllMatch、AnyMatch、NoneMatch、Count、FindFirst、FindLast 196 | 197 |   这些方法,均属于这里说的简单结果终止方法。代码如下: 198 | 199 | ```go 200 | package todocoder 201 | 202 | func TestSimple(t *testing.T) { 203 | allMatch := stream.Of( 204 | TestItem{itemNum: 7, itemValue: "item7"}, 205 | TestItem{itemNum: 6, itemValue: "item6"}, 206 | TestItem{itemNum: 8, itemValue: "item8"}, 207 | TestItem{itemNum: 1, itemValue: "item1"}, 208 | ).AllMatch(func(item TestItem) bool { 209 | // 返回此流中是否全都==1 210 | return item.itemNum == 1 211 | }) 212 | fmt.Println(allMatch) 213 | 214 | anyMatch := stream.Of( 215 | TestItem{itemNum: 7, itemValue: "item7"}, 216 | TestItem{itemNum: 6, itemValue: "item6"}, 217 | TestItem{itemNum: 8, itemValue: "item8"}, 218 | TestItem{itemNum: 1, itemValue: "item1"}, 219 | ).Filter(func(item TestItem) bool { 220 | return item.itemNum != 1 221 | }).AnyMatch(func(item TestItem) bool { 222 | // 返回此流中是否存在 == 8的 223 | return item.itemNum == 8 224 | }) 225 | fmt.Println(anyMatch) 226 | 227 | noneMatch := stream.Of( 228 | TestItem{itemNum: 7, itemValue: "item7"}, 229 | TestItem{itemNum: 6, itemValue: "item6"}, 230 | TestItem{itemNum: 8, itemValue: "item8"}, 231 | TestItem{itemNum: 1, itemValue: "item1"}, 232 | ).Filter(func(item TestItem) bool { 233 | return item.itemNum != 1 234 | }).NoneMatch(func(item TestItem) bool { 235 | // 返回此流中是否全部不等于8 236 | return item.itemNum == 8 237 | }) 238 | fmt.Println(noneMatch) 239 | 240 | resFirst := stream.Of( 241 | TestItem{itemNum: 1, itemValue: "item1"}, 242 | TestItem{itemNum: 2, itemValue: "item2"}, 243 | TestItem{itemNum: 3, itemValue: "item3"}, 244 | ).FindFirst() 245 | fmt.Println(resFirst.Get()) 246 | 247 | resLast := stream.Of( 248 | TestItem{itemNum: 1, itemValue: "item1"}, 249 | TestItem{itemNum: 2, itemValue: "item2"}, 250 | TestItem{itemNum: 3, itemValue: "item3"}, 251 | ).FindLast() 252 | fmt.Println(resLast.Get()) 253 | } 254 | ``` 255 | 256 | 结果: 257 | 258 | ``` 259 | false 260 | true 261 | false 262 | {1 item1} true 263 | {3 item3} true 264 | ``` 265 | 266 | ### Map、FlatMap 267 | 268 | Map与FlatMap都是用于转换已有的元素为其它元素,区别点在于: 269 | 270 | 1. Map 按照条件将已有元素转换为另一个对象类型,一对一逻辑 271 | 2. FlatMap 按照条件将已有元素转换为另一个对象类型,一对多逻辑 272 | 273 | 比如我要把 int 1 转为 TestItem{itemNum: 1, itemValue: "item1"} 274 | 275 | ```go 276 | package todocoder 277 | 278 | func TestMap(t *testing.T) { 279 | res := stream.Of([]int{1, 2, 3, 4, 7}...).Map(func(item int) any { 280 | return TestItem{ 281 | itemNum: item, 282 | itemValue: fmt.Sprintf("item%d", item), 283 | } 284 | }).Collect(collectors.ToSlice[any]()) 285 | fmt.Println(res) 286 | } 287 | ``` 288 | 289 | ``` 290 | [{1 item1} {2 item2} {3 item3} {4 item4} {7 item7}] 291 | ``` 292 | 293 | 那如果我要把两个字符串["wo shi todocoder","ha ha ha"] 转为 ["wo","shi","todocoder","ha","ha","ha"] 294 | 用Map就不行了,这就需要用到FlatMap了 295 | 296 | ```go 297 | package todocoder 298 | 299 | func TestFlatMap(t *testing.T) { 300 | // 把两个字符串["wo shi todocoder","ha ha ha"] 转为 ["wo","shi","todocoder","ha","ha","ha"] 301 | res := stream.Of([]string{"wo shi todocoder", "ha ha ha"}...).FlatMap(func(s string) stream.Stream[any] { 302 | return stream.OfFrom(func(source chan<- any) { 303 | for _, str := range strings.Split(s, " ") { 304 | source <- str 305 | } 306 | }) 307 | }).Collect(collectors.ToSlice[any]()) 308 | fmt.Println(res) 309 | } 310 | ``` 311 | 312 | ``` 313 | [wo shi todocoder ha ha ha] 314 | ``` 315 | 316 | > **注意:这里需要补充一句,只要经过Map或者FlatMap 处理后,类型就会统一变成 `any`了,而不是 泛型`T`,如需要强制类型处理,需要手动转换一下 317 | > 这个原因是Go泛型的局限性导致的,不能在struct 方法中定义其他类型的泛型,这块看后续官方是否支持了** 318 | > 319 | > 可以看如下代码 320 | 321 | ```go 322 | package todocoder 323 | 324 | func TestMap(t *testing.T) { 325 | res := stream.Of( 326 | TestItem{itemNum: 3, itemValue: "item3"}, 327 | ).FlatMap(func(item TestItem) stream.Stream[any] { 328 | return Of[any]( 329 | TestItem{itemNum: item.itemNum * 10, itemValue: fmt.Sprintf("%s+%d", item.itemValue, item.itemNum)}, 330 | TestItem{itemNum: item.itemNum * 20, itemValue: fmt.Sprintf("%s+%d", item.itemValue, item.itemNum)}, 331 | ) 332 | }).Map(func(item any) any { 333 | // 这里需要类型转换 334 | ite := item.(TestItem) 335 | return ToTestItem{ 336 | itemNum: ite.itemNum, 337 | itemValue: ite.itemValue, 338 | } 339 | }).Collect(collectors.ToSlice[any]()) 340 | fmt.Println(res) 341 | } 342 | ``` 343 | 344 | ### collectors.ToMap、collectors.GroupBy 345 | 346 |   这两个是相对复杂的终止方法,`ToMap` 是类似于Java stream流中`Collectors.toMap()`可以把切片数组转换成 切片map, `GroupBy` 347 | 类似于Java stream中 `Collectors.groupingby()`方法,按某个维度来分组 348 | 349 | 我有如下切片列表: 350 | 351 | *TestItem{itemNum: 1, itemValue: "item1"},* 352 | *TestItem{itemNum: 2, itemValue: "item2"},* 353 | *TestItem{itemNum: 2, itemValue: "item3"}* 354 | 355 | 1. 第一个需求是:把这个列表按 itemNum为Key, itemValue 为 value转换成Map 356 | 2. 第二个需求是:把这个列表按 itemNum为Key, 分组后转换成Map 357 | 我们看一下代码: 358 | 359 | ```go 360 | package todocoder 361 | 362 | func TestToMap(t *testing.T) { 363 | // 第一个需求 364 | resMap := Of( 365 | TestItem{itemNum: 1, itemValue: "item1"}, 366 | TestItem{itemNum: 2, itemValue: "item2"}, 367 | TestItem{itemNum: 2, itemValue: "item3"}, 368 | ).Collect(collectors.ToMap[TestItem](func(t TestItem) any { 369 | return t.itemNum 370 | }, func(item TestItem) any { 371 | return item.itemValue 372 | }, func(oldV, newV any) any { 373 | return oldV 374 | })) 375 | fmt.Println("第一个需求:") 376 | fmt.Println(resMap) 377 | // 第二个需求 378 | resGroup := Of( 379 | TestItem{itemNum: 1, itemValue: "item1"}, 380 | TestItem{itemNum: 2, itemValue: "item2"}, 381 | TestItem{itemNum: 2, itemValue: "item3"}, 382 | ).Collect(collectors.GroupingBy(func(t TestItem) any { 383 | return t.itemNum 384 | }, func(t TestItem) any { 385 | return t 386 | })) 387 | fmt.Println("第二个需求:") 388 | fmt.Println(resGroup) 389 | } 390 | ``` 391 | 392 | ``` 393 | 第一个需求: 394 | map[1:item1 2:item2] 395 | 第二个需求: 396 | map[1:[{1 item1}] 2:[{2 item2} {2 item3}]] 397 | ``` 398 | 399 | ### 转换函数 400 | 401 | #### Map、FlatMap(无需断言) 402 | 403 | Map与FlatMap都是用于转换已有的元素为其它元素,区别点在于: 404 | 405 | 1. Map 按照条件将已有元素转换为另一个对象类型,一对一逻辑 406 | 2. FlatMap 按照条件将已有元素转换为另一个对象类型,一对多逻辑 407 | 408 | 比如我要把 TestItem{itemNum: 1, itemValue: "item1"} 转换为ToTestItem{itemNum: 1, itemValue: "item1"},并且把一个元素按照一定的规则扩展成两个元素,可以通过如下的代码来实现 409 | 410 | ```go 411 | func TestFlatMap(t *testing.T) { 412 | res := stream.Map(stream.Of( 413 | TestItem{itemNum: 1, itemValue: "item1"}, 414 | TestItem{itemNum: 2, itemValue: "item2"}, 415 | TestItem{itemNum: 3, itemValue: "item3"}, 416 | ).FlatMap(func(item TestItem) Stream[TestItem] { 417 | return Of[TestItem]( 418 | TestItem{itemNum: item.itemNum * 10, itemValue: fmt.Sprintf("%s+%d", item.itemValue, item.itemNum)}, 419 | TestItem{itemNum: item.itemNum * 20, itemValue: fmt.Sprintf("%s+%d", item.itemValue, item.itemNum)}, 420 | ) 421 | }), func(item TestItem) ToTestItem { 422 | return ToTestItem{ 423 | itemNum: item.itemNum, 424 | itemValue: item.itemValue, 425 | } 426 | }).ToSlice() 427 | fmt.Println(res) 428 | } 429 | ``` 430 | 431 | #### GroupingBy() (使用结果无需断言) 432 | 需求: 433 | 班级有一组学号{1,2,3,....,12},对应12个人的信息在内存里面存着,把这学号转换成具体的 Student 类,过滤掉 Score 为 1的,并且按评分 Score 分组,并且对各组按照 Age 降序排列 434 | 435 | ```go 436 | 437 | studentMap := map[int]Student{ 438 | 1: {Num: 1, Name: "小明", Score: 3, Age: 26}, 439 | 2: {Num: 2, Name: "小红", Score: 4, Age: 27}, 440 | 3: {Num: 3, Name: "小李", Score: 5, Age: 19}, 441 | 4: {Num: 4, Name: "老王", Score: 1, Age: 23}, 442 | 5: {Num: 5, Name: "小王", Score: 2, Age: 29}, 443 | 6: {Num: 6, Name: "小绿", Score: 2, Age: 24}, 444 | 7: {Num: 7, Name: "小蓝", Score: 3, Age: 29}, 445 | 8: {Num: 8, Name: "小橙", Score: 3, Age: 30}, 446 | 9: {Num: 9, Name: "小黄", Score: 4, Age: 22}, 447 | 10: {Num: 10, Name: "小黑", Score: 5, Age: 21}, 448 | 11: {Num: 11, Name: "小紫", Score: 3, Age: 32}, 449 | 12: {Num: 12, Name: "小刘", Score: 2, Age: 35}, 450 | } 451 | 452 | res := GroupingBy(Map(Of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), func(n int) Student { 453 | // 注意 这里的返回类型可以是目标类型了 454 | return studentMap[n] 455 | }).Filter(func(s Student) bool { 456 | // 这里过滤也不需要转换类型 457 | // 过滤掉1的 458 | return s.Score != 1 459 | }), func(t Student) int { 460 | return t.Score 461 | }, func(t Student) Student { 462 | return t 463 | }, func(t1 []Student) { 464 | // 按年龄降序排列 465 | sort.Slice(t1, func(i, j int) bool { 466 | return t1[i].Age > t1[j].Age 467 | }) 468 | }) 469 | println(res) 470 | ``` 471 | 472 | ## 最后 473 | 474 |   作为一个Java开发,用习惯了Stream操作,也没找到合适的轻量的stream框架,也不知道后续官方是否会出,在这之前,就先自己简单实现一个,后面遇到复杂的处理流程会持续的更新到上面 475 | 除了上面这些功能,还有并行流处理,有兴趣可以自行查看体验[测试类:stream_test](https://github.com/todocoder/go-stream/blob/master/stream/stream_test.go) 476 | 477 | 有什么问题可以留言,看到后第一时间回复 478 | -------------------------------------------------------------------------------- /collectors/collectors.go: -------------------------------------------------------------------------------- 1 | package collectors 2 | 3 | import ( 4 | "github.com/todocoder/go-stream/utils" 5 | ) 6 | 7 | /* 8 | Number 抽象出常用的运算类型 9 | */ 10 | type Number interface { 11 | int | int8 | int16 | int32 | int64 | float32 | float64 12 | } 13 | 14 | /* 15 | SumStatistics 这个struct 是一些简单统计的聚合类 16 | */ 17 | type SumStatistics[S Number] struct { 18 | Count int64 19 | Sum S 20 | Max S 21 | Min S 22 | Average float64 23 | } 24 | 25 | func (s SumStatistics[S]) GetCount() int64 { 26 | return s.Count 27 | } 28 | func (s SumStatistics[S]) GetSum() S { 29 | return s.Sum 30 | } 31 | func (s SumStatistics[S]) GetMax() S { 32 | return s.Max 33 | } 34 | func (s SumStatistics[S]) GetMin() S { 35 | return s.Min 36 | } 37 | func (s SumStatistics[S]) GetAverage() float64 { 38 | return s.Average 39 | } 40 | 41 | /* 42 | Statistic Collect 统计方法 43 | 可以和stream结合使用,如下的代码来使用: 44 | 45 | eg: 46 | 47 | type TestItem struct { 48 | itemNum int 49 | itemValue string 50 | } 51 | 52 | res := stream.Collect(stream.Of( 53 | 54 | TestItem{itemNum: 1, itemValue: "item1"}, 55 | TestItem{itemNum: 2, itemValue: "3tem21"}, 56 | TestItem{itemNum: 2, itemValue: "2tem22"}, 57 | TestItem{itemNum: 2, itemValue: "1tem22"}, 58 | TestItem{itemNum: 2, itemValue: "4tem23"}, 59 | TestItem{itemNum: 3, itemValue: "item3"}, 60 | ).MapToInt(func(t TestItem) int { 61 | return t.itemNum * 2 62 | }), collectors.Statistic[int]()) 63 | println(res.GetSum()) 64 | println(res.GetAverage()) 65 | */ 66 | func Statistic[T Number]() Collector[T, *SumStatistics[T], SumStatistics[T]] { 67 | return &DefaultCollector[T, *SumStatistics[T], SumStatistics[T]]{ 68 | supplier: func() *SumStatistics[T] { 69 | return &SumStatistics[T]{ 70 | Count: 0, 71 | Sum: 0, 72 | Max: 0, 73 | Min: 0, 74 | Average: 0, 75 | } 76 | }, 77 | accumulator: func(ts *SumStatistics[T], t T) { 78 | if ts.Count == 0 { 79 | ts.Min = t 80 | } 81 | ts.Count++ 82 | ts.Sum = ts.Sum + t 83 | ts.Max = utils.If(ts.Max > t, ts.Max, t) 84 | ts.Min = utils.If(ts.Min < t, ts.Min, t) 85 | c := utils.ToAny[float64](ts.Count) 86 | s := utils.ToAny[float64](ts.Sum) 87 | ts.Average = utils.If(ts.Count > 0, s/c, 0) 88 | }, 89 | function: func(t *SumStatistics[T]) SumStatistics[T] { 90 | return *t 91 | }, 92 | } 93 | } 94 | 95 | type Key interface { 96 | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64 | string | bool 97 | } 98 | 99 | /* 100 | ToMap 切片转Map的Collect方法, 结合stream使用: 101 | 102 | eg: 103 | 104 | type TestItemS struct { 105 | itemNum int `json:"itemNum"` 106 | itemValue string `json:"itemValue"` 107 | } 108 | 109 | res1 := stream.Collect(stream.Of( 110 | 111 | TestItemS{itemNum: 1, itemValue: "item1"}, 112 | TestItemS{itemNum: 2, itemValue: "item2"}, 113 | TestItemS{itemNum: 3, itemValue: "item3"}, 114 | TestItemS{itemNum: 4, itemValue: "item4"}, 115 | TestItemS{itemNum: 5, itemValue: "item4"}, 116 | TestItemS{itemNum: 4, itemValue: "item5"}, 117 | ), collectors.ToMap(func(t TestItemS) string { 118 | return t.itemValue 119 | }, func(item TestItemS) int { 120 | return item.itemNum 121 | }, func(v1, v2 int) int { 122 | return v2 123 | })) 124 | println(res1) 125 | */ 126 | func ToMap[T any, K Key, U any](keyMapper func(T) K, valueMapper func(T) U, opts ...func(oldV, newV U) U) Collector[T, map[K]U, map[K]U] { 127 | return &DefaultCollector[T, map[K]U, map[K]U]{ 128 | supplier: func() map[K]U { 129 | temp := make(map[K]U, 0) 130 | return temp 131 | }, 132 | accumulator: func(ts map[K]U, t T) { 133 | key := keyMapper(t) 134 | value := valueMapper(t) 135 | 136 | for _, opt := range opts { 137 | value = mapMerge(key, value, ts, opt) 138 | ts[key] = value 139 | return 140 | } 141 | ts[key] = value 142 | return 143 | }, 144 | function: func(t map[K]U) map[K]U { 145 | return t 146 | }, 147 | } 148 | } 149 | 150 | func mapMerge[K Key, U any](key K, value U, old map[K]U, opt func(U, U) U) U { 151 | oldV, ok := old[key] 152 | var newV U 153 | if !ok || opt == nil { 154 | newV = value 155 | } else { 156 | newV = opt(oldV, value) 157 | } 158 | return newV 159 | } 160 | 161 | /* 162 | GroupingBy 分组的Collect 方法,结合stream使用 163 | 164 | eg: 165 | 166 | res1 := stream.Collect(stream.Of( 167 | 168 | TestItem{itemNum: 1, itemValue: "item1"}, 169 | TestItem{itemNum: 2, itemValue: "item2"}, 170 | TestItem{itemNum: 3, itemValue: "item2"}, 171 | TestItem{itemNum: 5, itemValue: "item2"}, 172 | TestItem{itemNum: 2, itemValue: "item2"}, 173 | TestItem{itemNum: 4, itemValue: "item4"}, 174 | TestItem{itemNum: 0, itemValue: "item4"}, 175 | TestItem{itemNum: 4, itemValue: "item5"}, 176 | TestItem{itemNum: 9, itemValue: "item5"}, 177 | ).Filter(func(item TestItem) bool { 178 | if item.itemNum != 1 { 179 | return true 180 | } 181 | return false 182 | }), collectors.GroupingBy(func(t TestItem) string { 183 | return t.itemValue 184 | }, func(t TestItem) int { 185 | return t.itemNum 186 | }, func(t1 []int) { 187 | sort.Slice(t1, func(i, j int) bool { 188 | return t1[i] < t1[j] 189 | }) 190 | })) 191 | fmt.Println(res1) 192 | */ 193 | func GroupingBy[T any, K Key, U any](keyMapper func(T) K, valueMapper func(T) U, opts ...func([]U)) Collector[T, map[K][]U, map[K][]U] { 194 | return &DefaultCollector[T, map[K][]U, map[K][]U]{ 195 | supplier: func() map[K][]U { 196 | temp := make(map[K][]U, 0) 197 | return temp 198 | }, 199 | accumulator: func(ts map[K][]U, t T) { 200 | key := keyMapper(t) 201 | value := valueMapper(t) 202 | ts[key] = append(ts[key], value) 203 | return 204 | }, 205 | function: func(t map[K][]U) map[K][]U { 206 | for _, vs := range t { 207 | for _, opt := range opts { 208 | opt(vs) 209 | } 210 | } 211 | return t 212 | }, 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /collectors/static.go: -------------------------------------------------------------------------------- 1 | /** 2 | * See java: stream 3 | */ 4 | package collectors 5 | 6 | type Supplier[R any] func() R 7 | 8 | type Function[A any, R any] func(A) R 9 | 10 | type BiConsumer[A any, T any] func(A, T) 11 | 12 | type BinaryOperator[T any, A any, R any] func(T, A) R 13 | 14 | // Collector See java: stream 15 | // – the type of input elements to the reduction operation 16 | // – the mutable accumulation type of the reduction operation (often hidden as an implementation detail) 17 | // – the result type of the reduction operation 18 | type Collector[T any, A any, R any] interface { 19 | Supplier() Supplier[A] 20 | Accumulator() BiConsumer[A, T] 21 | Combiner() BiConsumer[R, R] 22 | Finisher() Function[A, R] 23 | BinaryOperator() BinaryOperator[T, A, R] 24 | } 25 | 26 | type DefaultCollector[T any, A any, R any] struct { 27 | supplier Supplier[A] 28 | accumulator BiConsumer[A, T] 29 | combiner BiConsumer[R, R] 30 | function Function[A, R] 31 | binaryOperator BinaryOperator[T, A, R] 32 | } 33 | 34 | func (s *DefaultCollector[T, A, R]) Supplier() Supplier[A] { return s.supplier } 35 | func (s *DefaultCollector[T, A, R]) Accumulator() BiConsumer[A, T] { return s.accumulator } 36 | func (s *DefaultCollector[T, A, R]) Combiner() BiConsumer[R, R] { return s.combiner } 37 | func (s *DefaultCollector[T, A, R]) Finisher() Function[A, R] { return s.function } 38 | func (s *DefaultCollector[T, A, R]) BinaryOperator() BinaryOperator[T, A, R] { return s.binaryOperator } 39 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/todocoder/go-stream 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /stream/extracted.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import "github.com/todocoder/go-stream/collectors" 4 | 5 | /* 6 | Map stream 流 类型转换方法 7 | 8 | eg: 9 | 10 | res := Map(Of( 11 | 12 | TestItem{itemNum: 1, itemValue: "item1"}, 13 | TestItem{itemNum: 2, itemValue: "item2"}, 14 | TestItem{itemNum: 3, itemValue: "item3"}, 15 | ), func(item TestItem) ToTestItem { 16 | return ToTestItem{ 17 | itemNum: item.itemNum, 18 | itemValue: item.itemValue, 19 | } 20 | }).ToSlice() 21 | fmt.Println(res) 22 | */ 23 | func Map[T any, R any](s Stream[T], mapper func(T) R) Stream[R] { 24 | mapped := make([]R, 0) 25 | 26 | for el := range s.source { 27 | mapped = append(mapped, mapper(el)) 28 | } 29 | return Of(mapped...) 30 | } 31 | 32 | func FlatMap[T any, R any](s Stream[T], mapper func(T) Stream[R]) Stream[R] { 33 | streams := make([]Stream[R], 0) 34 | s.ForEach(func(t T) { 35 | streams = append(streams, mapper(t)) 36 | }) 37 | 38 | newEl := make([]R, 0) 39 | for _, str := range streams { 40 | newEl = append(newEl, str.ToSlice()...) 41 | } 42 | 43 | return Of(newEl...) 44 | } 45 | 46 | func GroupingBy[T any, K string | int | int32 | int64, R any](s Stream[T], keyMapper func(T) K, valueMapper func(T) R, opts ...OptFunc[R]) map[K][]R { 47 | groups := make(map[K][]R) 48 | s.ForEach(func(t T) { 49 | key := keyMapper(t) 50 | groups[key] = append(groups[key], valueMapper(t)) 51 | }) 52 | for _, vs := range groups { 53 | for _, opt := range opts { 54 | opt(vs) 55 | } 56 | } 57 | return groups 58 | } 59 | 60 | func Collect[T any, A any, R any](s Stream[T], collector collectors.Collector[T, A, R]) R { 61 | temp := collector.Supplier()() 62 | for item := range s.source { 63 | collector.Accumulator()(temp, item) 64 | } 65 | return collector.Finisher()(temp) 66 | } 67 | -------------------------------------------------------------------------------- /stream/stream.go: -------------------------------------------------------------------------------- 1 | // See: zeromicro/go-zero/core/stream 2 | package stream 3 | 4 | import ( 5 | "fmt" 6 | "github.com/todocoder/go-stream/collectors" 7 | "github.com/todocoder/go-stream/utils" 8 | "runtime" 9 | "sort" 10 | "strings" 11 | "sync" 12 | ) 13 | 14 | type ( 15 | Stream[T any] struct { 16 | source <-chan T 17 | isParallel bool 18 | } 19 | 20 | Optional[T any] struct { 21 | v *T 22 | } 23 | 24 | // OptFunc 定义操作切片的函数 25 | OptFunc[T any] func([]T) 26 | ) 27 | 28 | func (o Optional[T]) IsPresent() bool { 29 | return o.v != nil 30 | } 31 | 32 | func (o Optional[T]) Get() (v T, ok bool) { 33 | if o.v == nil { 34 | return *new(T), false 35 | } 36 | return *o.v, true 37 | } 38 | func (o Optional[T]) IfPresent(fn func(T)) { 39 | if o.v != nil { 40 | fn(*o.v) 41 | } 42 | } 43 | 44 | func (o Optional[T]) OrElse(other T) T { 45 | if o.v == nil { 46 | return other 47 | } 48 | return *o.v 49 | } 50 | 51 | func (o Optional[T]) OrElseGet(fn func() T) T { 52 | if o.v == nil { 53 | return fn() 54 | } 55 | return *o.v 56 | } 57 | 58 | func newStream[T any](isParallel bool, items ...T) Stream[T] { 59 | source := make(chan T, len(items)) 60 | for _, item := range items { 61 | source <- item 62 | } 63 | close(source) 64 | return Range[T](source, isParallel) 65 | } 66 | 67 | func (s Stream[T]) Concat(others ...Stream[T]) Stream[T] { 68 | source := make(chan T) 69 | go func() { 70 | for item := range s.source { 71 | source <- item 72 | } 73 | for _, each := range others { 74 | each := each 75 | for item := range each.source { 76 | source <- item 77 | } 78 | } 79 | close(source) 80 | }() 81 | return Range(source, s.isParallel) 82 | } 83 | func (s Stream[T]) Count() (count int64) { 84 | for range s.source { 85 | count++ 86 | } 87 | return 88 | } 89 | 90 | func (s Stream[T]) Filter(fn func(item T) bool) Stream[T] { 91 | return s.Walk(func(item T, pipe chan<- T) { 92 | if fn(item) { 93 | pipe <- item 94 | } 95 | }) 96 | } 97 | 98 | func (s Stream[T]) Peek(fn func(item *T)) Stream[T] { 99 | return s.Walk(func(item T, pipe chan<- T) { 100 | fn(&item) 101 | pipe <- item 102 | }) 103 | } 104 | 105 | // PeekP Point Peek 106 | func (s Stream[T]) PeekP(fn func(item T)) Stream[T] { 107 | 108 | return s.Walk(func(item T, pipe chan<- T) { 109 | fn(item) 110 | pipe <- item 111 | }) 112 | } 113 | 114 | func (s Stream[T]) Limit(maxSize int64) Stream[T] { 115 | if maxSize < 0 { 116 | panic("n must not be negative") 117 | } 118 | source := make(chan T) 119 | go func() { 120 | var n int64 = 0 121 | for item := range s.source { 122 | if n < maxSize { 123 | source <- item 124 | n++ 125 | } else { 126 | break 127 | } 128 | } 129 | close(source) 130 | }() 131 | return Range(source, s.isParallel) 132 | } 133 | 134 | func (s Stream[T]) Skip(n int64) Stream[T] { 135 | if n < 0 { 136 | panic("n must not be negative") 137 | } 138 | if n == 0 { 139 | return s 140 | } 141 | source := make(chan T) 142 | go func() { 143 | for item := range s.source { 144 | n-- 145 | if n >= 0 { 146 | continue 147 | } else { 148 | source <- item 149 | } 150 | } 151 | close(source) 152 | }() 153 | 154 | return Range(source, s.isParallel) 155 | } 156 | 157 | // TakeWhile 获取满足fn 函数(从第一个开始(包括)),之前的数据 158 | func (s Stream[T]) TakeWhile(fn func(item T) bool) Stream[T] { 159 | source := make(chan T) 160 | go func() { 161 | for item := range s.source { 162 | source <- item 163 | if fn(item) { 164 | break 165 | } 166 | } 167 | close(source) 168 | }() 169 | return Range(source, s.isParallel) 170 | } 171 | 172 | // DropWhile 丢弃满足fn 函数(从第一个开始截取),之前的数据 173 | func (s Stream[T]) DropWhile(fn func(item T) bool) Stream[T] { 174 | source := make(chan T) 175 | go func() { 176 | // 是否在 满足fn的游标之前 177 | flag := true 178 | for item := range s.source { 179 | if fn(item) { 180 | flag = false 181 | } 182 | if flag { 183 | continue 184 | } 185 | source <- item 186 | } 187 | close(source) 188 | }() 189 | return Range(source, s.isParallel) 190 | } 191 | 192 | func (s Stream[T]) Distinct(fn func(item T) any) Stream[T] { 193 | source := make(chan T) 194 | GoSafe(func() { 195 | defer close(source) 196 | 197 | keys := make(map[any]struct{}) 198 | for item := range s.source { 199 | key := fn(item) 200 | if _, ok := keys[key]; !ok { 201 | source <- item 202 | keys[key] = struct{}{} 203 | } 204 | } 205 | }) 206 | return Range(source, s.isParallel) 207 | } 208 | 209 | func (s Stream[T]) Sorted(less func(a, b T) bool) Stream[T] { 210 | var items []T 211 | for item := range s.source { 212 | items = append(items, item) 213 | } 214 | sort.Slice(items, func(i, j int) bool { 215 | return less(items[i], items[j]) 216 | }) 217 | return Of(items...) 218 | } 219 | 220 | func (s Stream[T]) Reverse() Stream[T] { 221 | var items []T 222 | for item := range s.source { 223 | items = append(items, item) 224 | } 225 | for i := len(items)/2 - 1; i >= 0; i-- { 226 | opp := len(items) - 1 - i 227 | items[i], items[opp] = items[opp], items[i] 228 | } 229 | 230 | return Of(items...) 231 | } 232 | 233 | func (s Stream[T]) Max(comparator func(T, T) int) Optional[T] { 234 | max, ok := s.FindFirst().Get() 235 | if !ok { 236 | return Optional[T]{v: nil} 237 | } 238 | s.ForEach(func(t T) { 239 | if comparator(t, max) > 0 { 240 | max = t 241 | } 242 | }) 243 | return Optional[T]{v: &max} 244 | } 245 | 246 | func (s Stream[T]) Min(comparator func(T, T) int) Optional[T] { 247 | 248 | min, ok := s.FindFirst().Get() 249 | if !ok { 250 | return Optional[T]{v: nil} 251 | } 252 | s.ForEach(func(t T) { 253 | if comparator(t, min) < 0 { 254 | min = t 255 | } 256 | }) 257 | return Optional[T]{v: &min} 258 | } 259 | 260 | type SumIntStatistics[T int | int32 | int64] struct { 261 | Count int64 262 | Sum int64 263 | Max T 264 | Min T 265 | Average float64 266 | } 267 | 268 | func (s SumIntStatistics[T]) GetCount() int64 { 269 | return s.Count 270 | } 271 | 272 | func (s SumIntStatistics[T]) GetSum() int64 { 273 | return s.Sum 274 | } 275 | 276 | func (s SumIntStatistics[T]) GetMax() T { 277 | return s.Max 278 | } 279 | 280 | func (s SumIntStatistics[T]) GetMin() T { 281 | return s.Min 282 | } 283 | 284 | func (s SumIntStatistics[T]) GetAverage() float64 { 285 | return s.Average 286 | } 287 | 288 | func (s Stream[T]) SumIntStatistics() SumIntStatistics[int] { 289 | var cnt = 0 290 | var sum int64 291 | var max int 292 | var min int 293 | for item := range s.source { 294 | i := utils.ToAny[int](item) 295 | if cnt == 0 { 296 | min = i 297 | } 298 | cnt++ 299 | sum = sum + int64(i) 300 | max = utils.If(max > i, max, i) 301 | min = utils.If(min < i, min, i) 302 | } 303 | return SumIntStatistics[int]{ 304 | Count: int64(cnt), 305 | Sum: sum, 306 | Max: max, 307 | Min: min, 308 | Average: utils.If(cnt > 0, float64(sum)/float64(cnt), 0), 309 | } 310 | } 311 | 312 | func (s Stream[T]) SumInt32Statistics() SumIntStatistics[int32] { 313 | var cnt = 0 314 | var sum int64 315 | var max int32 316 | var min int32 317 | for item := range s.source { 318 | i := utils.ToAny[int32](item) 319 | if cnt == 0 { 320 | min = i 321 | } 322 | cnt++ 323 | sum = sum + int64(i) 324 | max = utils.If(max > i, max, i) 325 | min = utils.If(min < i, min, i) 326 | } 327 | return SumIntStatistics[int32]{ 328 | Count: int64(cnt), 329 | Sum: sum, 330 | Max: max, 331 | Min: min, 332 | Average: utils.If(cnt > 0, float64(sum)/float64(cnt), 0), 333 | } 334 | } 335 | 336 | func (s Stream[T]) SumInt64Statistics() SumIntStatistics[int64] { 337 | var cnt = 0 338 | var sum int64 339 | var max int64 340 | var min int64 341 | for item := range s.source { 342 | i := utils.ToAny[int64](item) 343 | if cnt == 0 { 344 | min = i 345 | } 346 | cnt++ 347 | sum = sum + i 348 | max = utils.If(max > i, max, i) 349 | min = utils.If(min < i, min, i) 350 | } 351 | return SumIntStatistics[int64]{ 352 | Count: int64(cnt), 353 | Sum: sum, 354 | Max: max, 355 | Min: min, 356 | Average: utils.If(cnt > 0, float64(sum)/float64(cnt), 0), 357 | } 358 | } 359 | 360 | type SumFloatStatistics[T float32 | float64] struct { 361 | Count int64 362 | Sum float64 363 | Max T 364 | Min T 365 | Average float64 366 | } 367 | 368 | func (s SumFloatStatistics[T]) GetCount() int64 { 369 | return s.Count 370 | } 371 | func (s SumFloatStatistics[T]) GetSum() float64 { 372 | return s.Sum 373 | } 374 | func (s SumFloatStatistics[T]) GetMax() T { 375 | return s.Max 376 | } 377 | func (s SumFloatStatistics[T]) GetMin() T { 378 | return s.Min 379 | } 380 | func (s SumFloatStatistics[T]) GetAverage() float64 { 381 | return s.Average 382 | } 383 | 384 | func (s Stream[T]) SumFloat32Statistics() SumFloatStatistics[float32] { 385 | var cnt = 0 386 | var sum float64 387 | var max float32 388 | var min float32 389 | for item := range s.source { 390 | i := utils.ToAny[float32](item) 391 | if cnt == 0 { 392 | min = i 393 | } 394 | cnt++ 395 | sum = sum + float64(i) 396 | max = utils.If(max > i, max, i) 397 | min = utils.If(min < i, min, i) 398 | } 399 | return SumFloatStatistics[float32]{ 400 | Count: int64(cnt), 401 | Sum: sum, 402 | Max: max, 403 | Min: min, 404 | Average: utils.If(cnt > 0, sum/float64(cnt), 0), 405 | } 406 | } 407 | 408 | func (s Stream[T]) SumFloat64Statistics() SumFloatStatistics[float64] { 409 | var cnt = 0 410 | var sum float64 411 | var max float64 412 | var min float64 413 | for item := range s.source { 414 | i := utils.ToAny[float64](item) 415 | if cnt == 0 { 416 | min = i 417 | } 418 | cnt++ 419 | sum = sum + i 420 | max = utils.If(max > i, max, i) 421 | min = utils.If(min < i, min, i) 422 | } 423 | return SumFloatStatistics[float64]{ 424 | Count: int64(cnt), 425 | Sum: sum, 426 | Max: max, 427 | Min: min, 428 | Average: utils.If(cnt > 0, sum/float64(cnt), 0), 429 | } 430 | } 431 | 432 | func (s Stream[T]) ForEach(fn func(item T)) { 433 | var workers = 1 434 | if s.isParallel { 435 | workers = runtime.NumCPU() * 2 436 | } 437 | //go func() { 438 | var wg sync.WaitGroup 439 | // 这里是个占位类型 440 | pool := make(chan struct{}, workers) 441 | for item := range s.source { 442 | val := item 443 | // 这里是个占位类型值 444 | pool <- struct{}{} 445 | wg.Add(1) 446 | GoSafe(func() { 447 | defer func() { 448 | wg.Done() 449 | <-pool 450 | }() 451 | fn(val) 452 | }) 453 | } 454 | wg.Wait() 455 | close(pool) 456 | } 457 | 458 | // Walk 让调用者处理每个Item,调用者可以根据给定的Item编写零个、一个或多个项目 459 | func (s Stream[T]) Walk(fn func(item T, pipe chan<- T)) Stream[T] { 460 | return s.walkLimited(fn) 461 | } 462 | 463 | // walkLimited 遍历工作的协程个数限制 464 | func (s Stream[T]) walkLimited(fn func(item T, pipe chan<- T)) Stream[T] { 465 | var workers = 1 466 | if s.isParallel { 467 | workers = runtime.NumCPU() * 2 468 | } 469 | pipe := make(chan T, workers) 470 | go func() { 471 | defer close(pipe) 472 | var wg sync.WaitGroup 473 | // 这里是个占位类型 474 | pool := make(chan struct{}, workers) 475 | for item := range s.source { 476 | val := item 477 | // 这里是个占位类型值 478 | pool <- struct{}{} 479 | wg.Add(1) 480 | GoSafe(func() { 481 | defer func() { 482 | wg.Done() 483 | <-pool 484 | }() 485 | fn(val, pipe) 486 | }) 487 | } 488 | wg.Wait() 489 | }() 490 | return Range(pipe, s.isParallel) 491 | } 492 | 493 | // AllMatch 返回此流中是否全都满足条件 494 | func (s Stream[T]) AllMatch(predicate func(T) bool) bool { 495 | // 非缓冲通道 496 | flag := make(chan bool) 497 | GoSafe(func() { 498 | tempFlag := true 499 | for item := range s.source { 500 | if !predicate(item) { 501 | go drain(s.source) 502 | tempFlag = false 503 | break 504 | } 505 | } 506 | flag <- tempFlag 507 | }) 508 | return <-flag 509 | } 510 | 511 | // AnyMatch 返回此流中是否存在元素满足所提供的条件 512 | func (s Stream[T]) AnyMatch(predicate func(T) bool) bool { 513 | flag := make(chan bool) 514 | GoSafe(func() { 515 | tempFlag := false 516 | for item := range s.source { 517 | if predicate(item) { 518 | go drain(s.source) 519 | tempFlag = true 520 | break 521 | } 522 | } 523 | flag <- tempFlag 524 | }) 525 | return <-flag 526 | } 527 | 528 | // NoneMatch 返回此流中是否全都不满足条件 529 | func (s Stream[T]) NoneMatch(predicate func(T) bool) bool { 530 | flag := make(chan bool) 531 | GoSafe(func() { 532 | tempFlag := true 533 | for item := range s.source { 534 | if predicate(item) { 535 | go drain(s.source) 536 | tempFlag = false 537 | break 538 | } 539 | } 540 | flag <- tempFlag 541 | }) 542 | 543 | return <-flag 544 | } 545 | 546 | func (s Stream[T]) FindFirst() Optional[T] { 547 | for item := range s.source { 548 | go drain(s.source) 549 | return Optional[T]{v: &item} 550 | } 551 | return Optional[T]{v: nil} 552 | } 553 | 554 | func (s Stream[T]) FindLast() Optional[T] { 555 | tempStream := s.Reverse() 556 | for item := range tempStream.source { 557 | go drain(tempStream.source) 558 | return Optional[T]{v: &item} 559 | } 560 | return Optional[T]{v: nil} 561 | } 562 | 563 | func (s Stream[T]) Reduce(accumulator func(T, T) T) Optional[T] { 564 | var cnt = 0 565 | var res T 566 | for item := range s.source { 567 | if cnt == 0 { 568 | cnt++ 569 | res = item 570 | continue 571 | } 572 | cnt++ 573 | res = accumulator(res, item) 574 | } 575 | if cnt == 0 { 576 | return Optional[T]{v: nil} 577 | } 578 | return Optional[T]{v: &res} 579 | } 580 | 581 | func (s Stream[T]) Joining(seq string) string { 582 | // assert 583 | var b strings.Builder 584 | for item := range s.source { 585 | if v, ok := any(item).(string); ok { 586 | b.WriteString(v) 587 | b.WriteString(seq) 588 | } 589 | } 590 | fullContent := b.String() 591 | if len(fullContent) < 1 { 592 | return fullContent 593 | } 594 | return fullContent[:b.Len()-len(seq)] 595 | } 596 | 597 | func (s Stream[T]) Map(fn func(item T) any) Stream[any] { 598 | return Map[T](s, fn) 599 | } 600 | 601 | func (s Stream[T]) MapToString(mapper func(T) string) Stream[string] { 602 | return Map[T](s, mapper) 603 | } 604 | 605 | func (s Stream[T]) MapToInt(mapper func(T) int) Stream[int] { 606 | return Map[T](s, mapper) 607 | } 608 | 609 | func (s Stream[T]) MapToInt32(mapper func(T) int32) Stream[int32] { 610 | return Map[T](s, mapper) 611 | } 612 | 613 | func (s Stream[T]) MapToInt64(mapper func(T) int64) Stream[int64] { 614 | return Map[T](s, mapper) 615 | } 616 | 617 | func (s Stream[T]) MapToFloat64(mapper func(T) float64) Stream[float64] { 618 | return Map[T](s, mapper) 619 | } 620 | 621 | func (s Stream[T]) MapToFloat32(mapper func(T) float32) Stream[float32] { 622 | return Map[T](s, mapper) 623 | } 624 | 625 | func (s Stream[T]) FlatMap(mapper func(T) Stream[T]) Stream[T] { 626 | return FlatMap[T](s, mapper) 627 | } 628 | 629 | func (s Stream[T]) FlatMapToString(mapper func(T) Stream[string]) Stream[string] { 630 | return FlatMap[T](s, mapper) 631 | } 632 | 633 | func (s Stream[T]) FlatMapToInt(mapper func(T) Stream[int]) Stream[int] { 634 | return FlatMap[T](s, mapper) 635 | } 636 | func (s Stream[T]) FlatMapToInt32(mapper func(T) Stream[int32]) Stream[int32] { 637 | return FlatMap[T](s, mapper) 638 | } 639 | func (s Stream[T]) FlatMapToInt64(mapper func(T) Stream[int64]) Stream[int64] { 640 | return FlatMap[T](s, mapper) 641 | } 642 | func (s Stream[T]) FlatMapToFloat64(mapper func(T) Stream[float64]) Stream[float64] { 643 | return FlatMap[T](s, mapper) 644 | } 645 | func (s Stream[T]) FlatMapToFloat32(mapper func(T) Stream[float32]) Stream[float32] { 646 | return FlatMap[T](s, mapper) 647 | } 648 | 649 | func (s Stream[T]) ToSlice() []T { 650 | r := make([]T, 0) 651 | for item := range s.source { 652 | r = append(r, item) 653 | } 654 | return r 655 | } 656 | 657 | func (s Stream[T]) ToMapString(keyMapper func(T) string, valueMapper func(T) T, opts ...func(oldV, newV T) T) map[string]T { 658 | res := make(map[string]T, 0) 659 | for item := range s.source { 660 | key := keyMapper(item) 661 | value := valueMapper(item) 662 | 663 | oldV, ok := res[key] 664 | 665 | if !ok || opts == nil || len(opts) < 1 { 666 | res[key] = value 667 | continue 668 | } 669 | var newV T 670 | for _, opt := range opts { 671 | if !ok || opt == nil { 672 | newV = value 673 | } else { 674 | newV = opt(oldV, value) 675 | } 676 | res[key] = newV 677 | } 678 | } 679 | return res 680 | } 681 | 682 | func (s Stream[T]) ToMapInt(keyMapper func(T) int, valueMapper func(T) T, opts ...func(oldV, newV T) T) map[int]T { 683 | res := make(map[int]T, 0) 684 | for item := range s.source { 685 | key := keyMapper(item) 686 | value := valueMapper(item) 687 | 688 | oldV, ok := res[key] 689 | if !ok || opts == nil || len(opts) < 1 { 690 | res[key] = value 691 | continue 692 | } 693 | var newV T 694 | for _, opt := range opts { 695 | if !ok || opt == nil { 696 | newV = value 697 | } else { 698 | newV = opt(oldV, value) 699 | } 700 | res[key] = newV 701 | } 702 | } 703 | return res 704 | } 705 | 706 | // Deprecated: This function is no longer recommended. Please use extracted.Collect() instead. 707 | func (s Stream[T]) Collect(collector collectors.Collector[T, T, any]) any { 708 | temp := collector.Supplier()() 709 | for item := range s.source { 710 | collector.Accumulator()(item, temp) 711 | } 712 | return collector.Finisher()(temp) 713 | } 714 | 715 | func drain[T any](channel <-chan T) { 716 | for range channel { 717 | } 718 | } 719 | 720 | func Of[T any](values ...T) Stream[T] { 721 | return newStream(false, values...) 722 | } 723 | 724 | func OfParallel[T any](values ...T) Stream[T] { 725 | s := newStream(true, values...) 726 | return s.Walk(func(item T, pipe chan<- T) { 727 | pipe <- item 728 | }) 729 | } 730 | func OfFrom[T any](generate func(source chan<- T)) Stream[T] { 731 | source := make(chan T) 732 | GoSafe(func() { 733 | defer close(source) 734 | generate(source) 735 | }) 736 | return Range[T](source, false) 737 | } 738 | 739 | func OfFromParallel[T any](generate func(source chan<- T)) Stream[T] { 740 | source := make(chan T) 741 | GoSafe(func() { 742 | defer close(source) 743 | generate(source) 744 | }) 745 | return Range[T](source, true) 746 | } 747 | 748 | func Range[T any](source <-chan T, isParallel bool) Stream[T] { 749 | return Stream[T]{ 750 | source: source, 751 | isParallel: isParallel, 752 | } 753 | } 754 | 755 | func (s Stream[T]) GroupingByString(groupFunc func(T) string, opts ...OptFunc[T]) map[string][]T { 756 | groups := make(map[string][]T) 757 | s.ForEach(func(t T) { 758 | key := groupFunc(t) 759 | groups[key] = append(groups[key], t) 760 | }) 761 | for _, vs := range groups { 762 | for _, opt := range opts { 763 | opt(vs) 764 | } 765 | } 766 | return groups 767 | } 768 | 769 | func (s Stream[T]) GroupingByInt(groupFunc func(T) int, opts ...OptFunc[T]) map[int][]T { 770 | groups := make(map[int][]T) 771 | s.ForEach(func(t T) { 772 | key := groupFunc(t) 773 | groups[key] = append(groups[key], t) 774 | }) 775 | for _, vs := range groups { 776 | for _, opt := range opts { 777 | opt(vs) 778 | } 779 | } 780 | return groups 781 | } 782 | 783 | // Concat 拼接流 784 | func Concat[T any](s Stream[T], others ...Stream[T]) Stream[T] { 785 | return s.Concat(others...) 786 | } 787 | 788 | func GoSafe(fn func()) { 789 | RequireNonNil(fn) 790 | go RunSafe(fn) 791 | } 792 | 793 | func RunSafe(fn func()) { 794 | RequireNonNil(fn) 795 | defer Recover() 796 | fn() 797 | } 798 | 799 | func Recover(cleanups ...func()) { 800 | for _, cleanup := range cleanups { 801 | cleanup() 802 | } 803 | 804 | if p := recover(); p != nil { 805 | fmt.Println(p) 806 | } 807 | } 808 | 809 | func RequireNonNil(obj any) { 810 | if obj == nil { 811 | panic("data must not be nil") 812 | } 813 | } 814 | 815 | func OfOptional[T any](t *T) Optional[T] { 816 | RequireNonNil(t) 817 | return Optional[T]{v: t} 818 | } 819 | 820 | func OfOptionalNilable[T any](t *T) Optional[T] { 821 | if t == nil { 822 | return Optional[T]{v: nil} 823 | } 824 | return Optional[T]{v: t} 825 | } 826 | -------------------------------------------------------------------------------- /stream/stream_test.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/todocoder/go-stream/collectors" 7 | "sort" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | type TestItem struct { 13 | itemNum int 14 | itemValue string 15 | } 16 | type TestItemS struct { 17 | itemNum int `json:"itemNum"` 18 | itemValue string `json:"itemValue"` 19 | } 20 | 21 | func TestStreamT(t *testing.T) { 22 | items := []TestItem{ 23 | {itemNum: 7, itemValue: "item7"}, {itemNum: 6, itemValue: "item6"}, 24 | {itemNum: 1, itemValue: "item1"}, {itemNum: 2, itemValue: "item2"}, 25 | {itemNum: 3, itemValue: "item3"}, {itemNum: 4, itemValue: "item4"}, 26 | {itemNum: 5, itemValue: "item5"}, {itemNum: 5, itemValue: "item5"}, 27 | {itemNum: 5, itemValue: "item5"}, {itemNum: 8, itemValue: "item8"}, 28 | } 29 | res := Of(items...).Filter(func(item TestItem) bool { 30 | // 过滤掉1的值 31 | return item.itemNum != 4 32 | }).Distinct(func(item TestItem) any { 33 | // 按itemNum 去重 34 | return item.itemNum 35 | }).Sorted(func(a, b TestItem) bool { 36 | // 按itemNum升序排序 37 | return a.itemNum < b.itemNum 38 | }).Skip(1).Limit(6).Reverse().ToSlice() 39 | fmt.Println(res) 40 | } 41 | func TestStream(t *testing.T) { 42 | // 把两个字符串["wo shi todocoder","ha ha ha"] 转为 ["wo","shi","todocoder","ha","ha","ha"] 43 | 44 | res := Of([]*string{nil}).Count() 45 | fmt.Println(res) 46 | var temp []string 47 | Of(temp...).ForEach(func(t string) { 48 | println(t) 49 | }) 50 | 51 | fmt.Println("end") 52 | } 53 | 54 | func TestFlatMap2(t *testing.T) { 55 | // 把两个字符串["wo shi todocoder","ha ha ha"] 转为 ["wo","shi","todocoder","ha","ha","ha"] 56 | res := Of([]string{"wo shi todocoder", "ha ha ha"}...).FlatMap(func(s string) Stream[string] { 57 | return OfFrom(func(source chan<- string) { 58 | for _, str := range strings.Split(s, " ") { 59 | source <- str 60 | } 61 | }) 62 | }).ToSlice() 63 | 64 | fmt.Println(res) 65 | } 66 | 67 | func TestMap2(t *testing.T) { 68 | res := Of([]int{1, 2, 3, 4, 7}...).Map(func(item int) any { 69 | return TestItem{ 70 | itemNum: item, 71 | itemValue: fmt.Sprintf("item%d", item), 72 | } 73 | }).ToSlice() 74 | fmt.Println(res) 75 | } 76 | 77 | type ToTestItem struct { 78 | itemNum int 79 | itemValue string 80 | } 81 | 82 | func TestOfFrom(t *testing.T) { 83 | OfFrom(func(source chan<- TestItem) { 84 | source <- TestItem{itemNum: 1, itemValue: "item1"} 85 | source <- TestItem{itemNum: 2, itemValue: "item2"} 86 | source <- TestItem{itemNum: 3, itemValue: "item3"} 87 | }).ForEach(func(item TestItem) { 88 | fmt.Print(item.itemNum) 89 | fmt.Println(item.itemValue) 90 | }) 91 | OfFromParallel(func(source chan<- TestItem) { 92 | source <- TestItem{itemNum: 1, itemValue: "item1"} 93 | source <- TestItem{itemNum: 2, itemValue: "item2"} 94 | source <- TestItem{itemNum: 3, itemValue: "item3"} 95 | }).ForEach(func(item TestItem) { 96 | fmt.Print(item.itemNum) 97 | fmt.Println(item.itemValue) 98 | }) 99 | } 100 | 101 | func TestForEach(t *testing.T) { 102 | Of( 103 | TestItem{itemNum: 1, itemValue: "item1"}, 104 | TestItem{itemNum: 2, itemValue: "item2"}, 105 | TestItem{itemNum: 3, itemValue: "item3"}, 106 | ).ForEach(func(item TestItem) { 107 | fmt.Print(item.itemNum) 108 | fmt.Println(item.itemValue) 109 | }) 110 | 111 | OfParallel( 112 | TestItem{itemNum: 1, itemValue: "item1"}, 113 | TestItem{itemNum: 2, itemValue: "item2"}, 114 | TestItem{itemNum: 3, itemValue: "item3"}, 115 | ).ForEach(func(item TestItem) { 116 | fmt.Print(item.itemNum) 117 | fmt.Println(item.itemValue) 118 | }) 119 | } 120 | 121 | func TestForEachAndPeek(t *testing.T) { 122 | // ForEach 123 | fmt.Println("---------------------ForEach---------------------") 124 | Of( 125 | TestItem{itemNum: 1, itemValue: "item1"}, 126 | TestItem{itemNum: 2, itemValue: "item2"}, 127 | TestItem{itemNum: 3, itemValue: "item3"}, 128 | ).ForEach(func(item TestItem) { 129 | fmt.Println(item.itemValue) 130 | }) 131 | // Peek 132 | fmt.Println("---------------------Peek---------------------") 133 | Of( 134 | TestItem{itemNum: 1, itemValue: "item1"}, 135 | TestItem{itemNum: 2, itemValue: "item2"}, 136 | TestItem{itemNum: 3, itemValue: "item3"}, 137 | ).Peek(func(item *TestItem) { 138 | item.itemValue = item.itemValue + "peek" 139 | }).ForEach(func(item TestItem) { 140 | fmt.Println(item.itemValue) 141 | }) 142 | 143 | // Peek Point 144 | fmt.Println("---------------------Peek Point---------------------") 145 | Of( 146 | &TestItem{itemNum: 1, itemValue: "item1"}, 147 | &TestItem{itemNum: 2, itemValue: "item2"}, 148 | &TestItem{itemNum: 3, itemValue: "item3"}, 149 | ).PeekP(func(item *TestItem) { 150 | item.itemValue = item.itemValue + "peekp" 151 | }).ForEach(func(item *TestItem) { 152 | fmt.Println(item.itemValue) 153 | }) 154 | } 155 | 156 | func TestPeek(t *testing.T) { 157 | Of( 158 | TestItem{itemNum: 1, itemValue: "item1"}, 159 | TestItem{itemNum: 2, itemValue: "item2"}, 160 | TestItem{itemNum: 3, itemValue: "item3"}, 161 | ).Peek(func(item *TestItem) { 162 | item.itemValue = item.itemValue + "peek" 163 | }).ForEach(func(item TestItem) { 164 | fmt.Print(item.itemNum) 165 | fmt.Println(item.itemValue) 166 | }) 167 | 168 | OfParallel( 169 | TestItem{itemNum: 1, itemValue: "item1"}, 170 | TestItem{itemNum: 2, itemValue: "item2"}, 171 | TestItem{itemNum: 3, itemValue: "item3"}, 172 | ).Peek(func(item *TestItem) { 173 | item.itemValue = item.itemValue + "peek" 174 | }).ForEach(func(item TestItem) { 175 | fmt.Print(item.itemNum) 176 | fmt.Println(item.itemValue) 177 | }) 178 | } 179 | 180 | func TestCount(t *testing.T) { 181 | res := Of( 182 | TestItem{itemNum: 1, itemValue: "item1"}, 183 | TestItem{itemNum: 2, itemValue: "item2"}, 184 | TestItem{itemNum: 3, itemValue: "item3"}, 185 | ).Count() 186 | fmt.Println(res) 187 | 188 | resP := OfParallel( 189 | TestItem{itemNum: 1, itemValue: "item1"}, 190 | TestItem{itemNum: 2, itemValue: "item2"}, 191 | TestItem{itemNum: 3, itemValue: "item3"}, 192 | ).Count() 193 | fmt.Println(resP) 194 | } 195 | 196 | func TestMaxMin(t *testing.T) { 197 | resFirst := Of( 198 | TestItem{itemNum: 1, itemValue: "item1"}, 199 | TestItem{itemNum: 2, itemValue: "item2"}, 200 | TestItem{itemNum: 3, itemValue: "item3"}, 201 | ).FindFirst() 202 | fmt.Println(resFirst.Get()) 203 | 204 | resLast := Of( 205 | TestItem{itemNum: 1, itemValue: "item1"}, 206 | TestItem{itemNum: 2, itemValue: "item2"}, 207 | TestItem{itemNum: 3, itemValue: "item3"}, 208 | ).FindLast() 209 | fmt.Println(resLast.Get()) 210 | 211 | resMax := Of( 212 | TestItem{itemNum: 1, itemValue: "item1"}, 213 | TestItem{itemNum: 2, itemValue: "item2"}, 214 | TestItem{itemNum: 3, itemValue: "item3"}, 215 | ).Max(func(item TestItem, item2 TestItem) int { 216 | if item.itemNum > item2.itemNum { 217 | return 1 218 | } else if item.itemNum == item2.itemNum { 219 | return 0 220 | } 221 | return -1 222 | }) 223 | fmt.Println(resMax.Get()) 224 | 225 | resMin := Of( 226 | TestItem{itemNum: 1, itemValue: "item1"}, 227 | TestItem{itemNum: 2, itemValue: "item2"}, 228 | TestItem{itemNum: 3, itemValue: "item3"}, 229 | ).Min(func(item TestItem, item2 TestItem) int { 230 | if item.itemNum > item2.itemNum { 231 | return 1 232 | } else if item.itemNum == item2.itemNum { 233 | return 0 234 | } 235 | return -1 236 | }) 237 | fmt.Println(resMin.Get()) 238 | temp := make([]TestItem, 0) 239 | resTemp := Of(temp...).Min(func(item TestItem, item2 TestItem) int { 240 | if item.itemNum > item2.itemNum { 241 | return 1 242 | } else if item.itemNum == item2.itemNum { 243 | return 0 244 | } 245 | return -1 246 | }) 247 | fmt.Println(resTemp.Get()) 248 | } 249 | 250 | func TestToAverage(t *testing.T) { 251 | resInt := Of( 252 | TestItem{itemNum: 1, itemValue: "item1"}, 253 | TestItem{itemNum: 2, itemValue: "item2"}, 254 | TestItem{itemNum: 3, itemValue: "item3"}, 255 | ).MapToInt(func(item TestItem) int { 256 | return item.itemNum 257 | }).SumIntStatistics() 258 | fmt.Println(resInt.GetSum()) 259 | fmt.Println(resInt.GetCount()) 260 | fmt.Println(resInt.GetMin()) 261 | fmt.Println(resInt.GetMax()) 262 | fmt.Println(resInt.GetAverage()) 263 | 264 | resFloat := Of( 265 | TestItem{itemNum: 1, itemValue: "item1"}, 266 | TestItem{itemNum: 2, itemValue: "item2"}, 267 | TestItem{itemNum: 3, itemValue: "item3"}, 268 | ).MapToFloat64(func(item TestItem) float64 { 269 | return float64(item.itemNum) 270 | }).SumIntStatistics() 271 | fmt.Println(resFloat.GetSum()) 272 | fmt.Println(resFloat.GetCount()) 273 | fmt.Println(resFloat.GetMin()) 274 | fmt.Println(resFloat.GetMax()) 275 | fmt.Println(resFloat.GetAverage()) 276 | } 277 | 278 | func TestSorted(t *testing.T) { 279 | resSorted := Of( 280 | TestItem{itemNum: 1, itemValue: "item1"}, 281 | TestItem{itemNum: 2, itemValue: "item2"}, 282 | TestItem{itemNum: 3, itemValue: "item3"}, 283 | ).Sorted(func(a, b TestItem) bool { 284 | // 降序 285 | return a.itemNum > b.itemNum 286 | }).ToSlice() 287 | fmt.Println(resSorted) 288 | } 289 | 290 | func TestReverse(t *testing.T) { 291 | resReverse := Of( 292 | TestItem{itemNum: 1, itemValue: "item1"}, 293 | TestItem{itemNum: 2, itemValue: "item2"}, 294 | TestItem{itemNum: 3, itemValue: "item3"}, 295 | ).Reverse().ToSlice() 296 | fmt.Println(resReverse) 297 | } 298 | 299 | func TestDistinct(t *testing.T) { 300 | resReverse := Of( 301 | TestItem{itemNum: 1, itemValue: "item1"}, 302 | TestItem{itemNum: 2, itemValue: "item2"}, 303 | TestItem{itemNum: 3, itemValue: "item3"}, 304 | TestItem{itemNum: 3, itemValue: "item4"}, 305 | TestItem{itemNum: 4, itemValue: "item4"}, 306 | ).Distinct(func(item TestItem) any { 307 | //return item.itemNum 308 | return item.itemValue 309 | }).ToSlice() 310 | fmt.Println(resReverse) 311 | } 312 | func TestSkip(t *testing.T) { 313 | resSkip := Of( 314 | TestItem{itemNum: 1, itemValue: "item1"}, 315 | TestItem{itemNum: 2, itemValue: "item2"}, 316 | TestItem{itemNum: 3, itemValue: "item3"}, 317 | TestItem{itemNum: 3, itemValue: "item4"}, 318 | TestItem{itemNum: 4, itemValue: "item4"}, 319 | ).Skip(1).ToSlice() 320 | fmt.Println(resSkip) 321 | } 322 | 323 | func TestLimit(t *testing.T) { 324 | resLimit := Of( 325 | TestItem{itemNum: 1, itemValue: "item1"}, 326 | TestItem{itemNum: 2, itemValue: "item2"}, 327 | TestItem{itemNum: 3, itemValue: "item3"}, 328 | TestItem{itemNum: 3, itemValue: "item4"}, 329 | TestItem{itemNum: 4, itemValue: "item4"}, 330 | ).Skip(1).Limit(7).ToSlice() 331 | fmt.Println(resLimit) 332 | } 333 | 334 | func TestReduce(t *testing.T) { 335 | res := Of( 336 | TestItem{itemNum: 1, itemValue: "item1"}, 337 | TestItem{itemNum: 2, itemValue: "item2"}, 338 | TestItem{itemNum: 3, itemValue: "item3"}, 339 | TestItem{itemNum: 4, itemValue: "item4"}, 340 | ).Filter(func(item TestItem) bool { 341 | if item.itemNum != 1 { 342 | return true 343 | } 344 | return false 345 | }).Reduce(func(item TestItem, item2 TestItem) TestItem { 346 | return TestItem{itemNum: item.itemNum + item2.itemNum, itemValue: fmt.Sprintf("%s_%s", item.itemValue, item2.itemValue)} 347 | }) 348 | fmt.Println(res.Get()) 349 | } 350 | 351 | func TestJoining(t *testing.T) { 352 | resE := Of( 353 | TestItem{itemNum: 1, itemValue: "item1"}, 354 | TestItem{itemNum: 2, itemValue: "item2"}, 355 | TestItem{itemNum: 3, itemValue: "item3"}, 356 | TestItem{itemNum: 4, itemValue: "item4"}, 357 | ).Joining("--") 358 | fmt.Println(resE) 359 | temp := make([]TestItem, 0) 360 | resEmpty := Of(temp...).Joining("--") 361 | fmt.Println(resEmpty) 362 | 363 | resR := Of( 364 | TestItem{itemNum: 1, itemValue: "item1"}, 365 | TestItem{itemNum: 2, itemValue: "item2"}, 366 | TestItem{itemNum: 3, itemValue: "item3"}, 367 | TestItem{itemNum: 4, itemValue: "item4"}, 368 | ).MapToString(func(a TestItem) string { 369 | return a.itemValue 370 | }).Joining("-") 371 | fmt.Println(resR) 372 | 373 | resRSeqE := Of( 374 | TestItem{itemNum: 1, itemValue: "item1"}, 375 | TestItem{itemNum: 2, itemValue: "item2"}, 376 | TestItem{itemNum: 3, itemValue: "item3"}, 377 | TestItem{itemNum: 4, itemValue: "item4"}, 378 | ).MapToString(func(a TestItem) string { 379 | return a.itemValue 380 | }).Joining("") 381 | fmt.Println(resRSeqE) 382 | 383 | resRE := Of( 384 | TestItem{itemNum: 1}, 385 | TestItem{itemNum: 2, itemValue: "item2"}, 386 | TestItem{itemNum: 3, itemValue: "item3"}, 387 | TestItem{itemNum: 4, itemValue: "item4"}, 388 | ).MapToString(func(a TestItem) string { 389 | return a.itemValue 390 | }).Joining("-") 391 | fmt.Println(resRE) 392 | } 393 | 394 | func TestToSlice(t *testing.T) { 395 | res := Of( 396 | TestItem{itemNum: 1, itemValue: "item1"}, 397 | TestItem{itemNum: 2, itemValue: "item2"}, 398 | TestItem{itemNum: 3, itemValue: "item3"}, 399 | TestItem{itemNum: 4, itemValue: "item4"}, 400 | ).Filter(func(item TestItem) bool { 401 | if item.itemNum != 1 { 402 | return true 403 | } 404 | return false 405 | }).ToSlice() 406 | fmt.Println(res) 407 | } 408 | 409 | func TestToMap(t *testing.T) { 410 | res := Of( 411 | TestItem{itemNum: 1, itemValue: "item1"}, 412 | TestItem{itemNum: 2, itemValue: "item2"}, 413 | TestItem{itemNum: 3, itemValue: "item3"}, 414 | TestItem{itemNum: 4, itemValue: "item4"}, 415 | TestItem{itemNum: 4, itemValue: "item5"}, 416 | ).Filter(func(item TestItem) bool { 417 | if item.itemNum != 1 { 418 | return true 419 | } 420 | return false 421 | }).ToMapInt(func(t TestItem) int { 422 | return t.itemNum 423 | }, func(item TestItem) TestItem { 424 | return item 425 | }) 426 | fmt.Println(res) 427 | } 428 | 429 | func TestToMap1(t *testing.T) { 430 | res0 := Collect(Of( 431 | TestItemS{itemNum: 1, itemValue: "item1"}, 432 | TestItemS{itemNum: 2, itemValue: "item2"}, 433 | TestItemS{itemNum: 3, itemValue: "item3"}, 434 | TestItemS{itemNum: 4, itemValue: "item4"}, 435 | TestItemS{itemNum: 4, itemValue: "item4"}, 436 | TestItemS{itemNum: 4, itemValue: "item5"}, 437 | ), collectors.ToMap(func(t TestItemS) string { 438 | return t.itemValue 439 | }, func(item TestItemS) int { 440 | return item.itemNum 441 | })) 442 | println(res0) 443 | 444 | res1 := Collect(Of( 445 | TestItemS{itemNum: 1, itemValue: "item1"}, 446 | TestItemS{itemNum: 2, itemValue: "item2"}, 447 | TestItemS{itemNum: 3, itemValue: "item3"}, 448 | TestItemS{itemNum: 4, itemValue: "item4"}, 449 | TestItemS{itemNum: 5, itemValue: "item4"}, 450 | TestItemS{itemNum: 4, itemValue: "item5"}, 451 | ), collectors.ToMap(func(t TestItemS) string { 452 | return t.itemValue 453 | }, func(item TestItemS) int { 454 | return item.itemNum 455 | }, func(v1, v2 int) int { 456 | return v2 457 | })) 458 | println(res1) 459 | v, k := json.Marshal(res0) 460 | 461 | fmt.Println(v) 462 | fmt.Println(k) 463 | } 464 | 465 | func TestToMapOpt(t *testing.T) { 466 | res := Collect(Of( 467 | TestItem{itemNum: 1, itemValue: "item1"}, 468 | TestItem{itemNum: 2, itemValue: "item2"}, 469 | TestItem{itemNum: 3, itemValue: "item3"}, 470 | TestItem{itemNum: 4, itemValue: "item4"}, 471 | TestItem{itemNum: 4, itemValue: "item5"}, 472 | ).Filter(func(item TestItem) bool { 473 | if item.itemNum != 1 { 474 | return true 475 | } 476 | return false 477 | }), collectors.ToMap(func(t TestItem) int { 478 | return t.itemNum 479 | }, func(item TestItem) string { 480 | return item.itemValue 481 | }, func(oldV, newV string) string { 482 | return oldV 483 | })) 484 | fmt.Println(res) 485 | } 486 | 487 | func TestToGroupBy(t *testing.T) { 488 | res := Collect(Of( 489 | TestItem{itemNum: 1, itemValue: "item1"}, 490 | TestItem{itemNum: 2, itemValue: "item2"}, 491 | TestItem{itemNum: 3, itemValue: "item3"}, 492 | TestItem{itemNum: 4, itemValue: "item4"}, 493 | TestItem{itemNum: 4, itemValue: "item5"}, 494 | ).Filter(func(item TestItem) bool { 495 | if item.itemNum != 1 { 496 | return true 497 | } 498 | return false 499 | }), collectors.GroupingBy(func(t TestItem) int { 500 | return t.itemNum 501 | }, func(t TestItem) string { 502 | return t.itemValue 503 | })) 504 | fmt.Println(res) 505 | 506 | res1 := Collect(Of( 507 | TestItem{itemNum: 1, itemValue: "item1"}, 508 | TestItem{itemNum: 2, itemValue: "item2"}, 509 | TestItem{itemNum: 3, itemValue: "item2"}, 510 | TestItem{itemNum: 5, itemValue: "item2"}, 511 | TestItem{itemNum: 2, itemValue: "item2"}, 512 | TestItem{itemNum: 4, itemValue: "item4"}, 513 | TestItem{itemNum: 0, itemValue: "item4"}, 514 | TestItem{itemNum: 4, itemValue: "item5"}, 515 | TestItem{itemNum: 9, itemValue: "item5"}, 516 | ).Filter(func(item TestItem) bool { 517 | if item.itemNum != 1 { 518 | return true 519 | } 520 | return false 521 | }), collectors.GroupingBy(func(t TestItem) string { 522 | return t.itemValue 523 | }, func(t TestItem) int { 524 | return t.itemNum 525 | }, func(t1 []int) { 526 | sort.Slice(t1, func(i, j int) bool { 527 | return t1[i] < t1[j] 528 | }) 529 | })) 530 | fmt.Println(res1) 531 | 532 | res2 := GroupingBy(Of( 533 | TestItem{itemNum: 1, itemValue: "item1"}, 534 | TestItem{itemNum: 2, itemValue: "item2"}, 535 | TestItem{itemNum: 3, itemValue: "item2"}, 536 | TestItem{itemNum: 5, itemValue: "item2"}, 537 | TestItem{itemNum: 2, itemValue: "item2"}, 538 | TestItem{itemNum: 4, itemValue: "item4"}, 539 | TestItem{itemNum: 0, itemValue: "item4"}, 540 | TestItem{itemNum: 4, itemValue: "item5"}, 541 | TestItem{itemNum: 9, itemValue: "item5"}, 542 | ).Filter(func(item TestItem) bool { 543 | if item.itemNum != 1 { 544 | return true 545 | } 546 | return false 547 | }), func(t TestItem) string { 548 | return t.itemValue 549 | }, func(t TestItem) int { 550 | return t.itemNum 551 | }, func(t1 []int) { 552 | sort.Slice(t1, func(i, j int) bool { 553 | return t1[i] < t1[j] 554 | }) 555 | }) 556 | fmt.Println(res2) 557 | 558 | } 559 | 560 | func TestFilter(t *testing.T) { 561 | Of( 562 | TestItem{itemNum: 1, itemValue: "item1"}, 563 | TestItem{itemNum: 2, itemValue: "item2"}, 564 | TestItem{itemNum: 3, itemValue: "item3"}, 565 | TestItem{itemNum: 4, itemValue: "item4"}, 566 | TestItem{itemNum: 5, itemValue: "item5"}, 567 | TestItem{itemNum: 6, itemValue: "item6"}, 568 | TestItem{itemNum: 7, itemValue: "item7"}, 569 | TestItem{itemNum: 8, itemValue: "item8"}, 570 | TestItem{itemNum: 9, itemValue: "item9"}, 571 | ).Filter(func(item TestItem) bool { 572 | if item.itemNum%2 != 0 { 573 | return true 574 | } 575 | return false 576 | }).ForEach(func(item TestItem) { 577 | fmt.Print(item.itemNum) 578 | fmt.Println(item.itemValue) 579 | }) 580 | } 581 | 582 | func TestMap(t *testing.T) { 583 | res := Of( 584 | TestItem{itemNum: 1, itemValue: "item1"}, 585 | TestItem{itemNum: 2, itemValue: "item2"}, 586 | TestItem{itemNum: 3, itemValue: "item3"}, 587 | TestItem{itemNum: 4, itemValue: "item4"}, 588 | TestItem{itemNum: 5, itemValue: "item5"}, 589 | TestItem{itemNum: 6, itemValue: "item6"}, 590 | TestItem{itemNum: 7, itemValue: "item7"}, 591 | TestItem{itemNum: 8, itemValue: "item8"}, 592 | TestItem{itemNum: 9, itemValue: "item9"}, 593 | ).Filter(func(item TestItem) bool { 594 | if item.itemNum != 1 { 595 | return true 596 | } 597 | return false 598 | }).Map(func(item TestItem) any { 599 | return ToTestItem{ 600 | itemNum: item.itemNum, 601 | itemValue: item.itemValue, 602 | } 603 | }).ToSlice() 604 | fmt.Println(res) 605 | } 606 | 607 | func TestFlatMap(t *testing.T) { 608 | res := Map(Of( 609 | TestItem{itemNum: 1, itemValue: "item1"}, 610 | TestItem{itemNum: 2, itemValue: "item2"}, 611 | TestItem{itemNum: 3, itemValue: "item3"}, 612 | ).Filter(func(item TestItem) bool { 613 | if item.itemNum != 1 { 614 | return true 615 | } 616 | return false 617 | }).FlatMap(func(item TestItem) Stream[TestItem] { 618 | return Of[TestItem]( 619 | TestItem{itemNum: item.itemNum * 10, itemValue: fmt.Sprintf("%s+%d", item.itemValue, item.itemNum)}, 620 | TestItem{itemNum: item.itemNum * 20, itemValue: fmt.Sprintf("%s+%d", item.itemValue, item.itemNum)}, 621 | ) 622 | }), func(item TestItem) ToTestItem { 623 | return ToTestItem{ 624 | itemNum: item.itemNum, 625 | itemValue: item.itemValue, 626 | } 627 | }).ToSlice() 628 | fmt.Println(res) 629 | } 630 | 631 | func TestConcatenate(t *testing.T) { 632 | resConcat := Concat(Of( 633 | TestItem{itemNum: 1, itemValue: "item1"}, 634 | TestItem{itemNum: 2, itemValue: "item2"}, 635 | ), Of( 636 | TestItem{itemNum: 5, itemValue: "item5"}, 637 | TestItem{itemNum: 6, itemValue: "item6"}, 638 | ), Of( 639 | TestItem{itemNum: 3, itemValue: "item3"}, 640 | TestItem{itemNum: 4, itemValue: "item4"}, 641 | )).ToSlice() 642 | fmt.Println(resConcat) 643 | } 644 | 645 | func TestSimple(t *testing.T) { 646 | allMatch := Of( 647 | TestItem{itemNum: 7, itemValue: "item7"}, 648 | TestItem{itemNum: 6, itemValue: "item6"}, 649 | TestItem{itemNum: 8, itemValue: "item8"}, 650 | TestItem{itemNum: 1, itemValue: "item1"}, 651 | ).AllMatch(func(item TestItem) bool { 652 | // 返回此流中是否全都==1 653 | return item.itemNum == 1 654 | }) 655 | fmt.Println(allMatch) 656 | 657 | anyMatch := Of( 658 | TestItem{itemNum: 7, itemValue: "item7"}, 659 | TestItem{itemNum: 6, itemValue: "item6"}, 660 | TestItem{itemNum: 8, itemValue: "item8"}, 661 | TestItem{itemNum: 1, itemValue: "item1"}, 662 | ).Filter(func(item TestItem) bool { 663 | return item.itemNum != 1 664 | }).AnyMatch(func(item TestItem) bool { 665 | // 返回此流中是否存在 == 8的 666 | return item.itemNum == 8 667 | }) 668 | fmt.Println(anyMatch) 669 | 670 | noneMatch := Of( 671 | TestItem{itemNum: 7, itemValue: "item7"}, 672 | TestItem{itemNum: 6, itemValue: "item6"}, 673 | TestItem{itemNum: 8, itemValue: "item8"}, 674 | TestItem{itemNum: 1, itemValue: "item1"}, 675 | ).Filter(func(item TestItem) bool { 676 | return item.itemNum != 1 677 | }).NoneMatch(func(item TestItem) bool { 678 | // 返回此流中是否全部不等于8 679 | return item.itemNum == 8 680 | }) 681 | fmt.Println(noneMatch) 682 | 683 | resFirst := Of( 684 | TestItem{itemNum: 1, itemValue: "item1"}, 685 | TestItem{itemNum: 2, itemValue: "item2"}, 686 | TestItem{itemNum: 3, itemValue: "item3"}, 687 | ).FindFirst() 688 | fmt.Println(resFirst.Get()) 689 | 690 | resLast := Of( 691 | TestItem{itemNum: 1, itemValue: "item1"}, 692 | TestItem{itemNum: 2, itemValue: "item2"}, 693 | TestItem{itemNum: 3, itemValue: "item3"}, 694 | ).FindLast() 695 | fmt.Println(resLast.Get()) 696 | } 697 | 698 | func TestGroupby(t *testing.T) { 699 | res := Of( 700 | TestItem{itemNum: 1, itemValue: "item1"}, 701 | TestItem{itemNum: 2, itemValue: "3tem21"}, 702 | TestItem{itemNum: 2, itemValue: "2tem22"}, 703 | TestItem{itemNum: 2, itemValue: "1tem22"}, 704 | TestItem{itemNum: 2, itemValue: "4tem23"}, 705 | TestItem{itemNum: 3, itemValue: "item3"}, 706 | ).GroupingByInt(func(t TestItem) int { 707 | return t.itemNum 708 | }) 709 | fmt.Println(res) 710 | res1 := Of( 711 | TestItem{itemNum: 1, itemValue: "item1"}, 712 | TestItem{itemNum: 2, itemValue: "item2"}, 713 | TestItem{itemNum: 6, itemValue: "item2"}, 714 | TestItem{itemNum: 3, itemValue: "item2"}, 715 | TestItem{itemNum: 3, itemValue: "item3"}, 716 | ).GroupingByString(func(t TestItem) string { 717 | return t.itemValue 718 | }, func(t1 []TestItem) { 719 | sort.Slice(t1, func(i, j int) bool { 720 | return t1[i].itemNum < t1[j].itemNum 721 | }) 722 | }) 723 | fmt.Println(res1) 724 | } 725 | 726 | func TestStatistic(t *testing.T) { 727 | res := Collect(Of( 728 | TestItem{itemNum: 1, itemValue: "item1"}, 729 | TestItem{itemNum: 2, itemValue: "3tem21"}, 730 | TestItem{itemNum: 2, itemValue: "2tem22"}, 731 | TestItem{itemNum: 2, itemValue: "1tem22"}, 732 | TestItem{itemNum: 2, itemValue: "4tem23"}, 733 | TestItem{itemNum: 3, itemValue: "item3"}, 734 | ).MapToInt(func(t TestItem) int { 735 | return t.itemNum * 2 736 | }), collectors.Statistic[int]()) 737 | 738 | println(res.GetAverage()) 739 | res1 := Of([]int64{1, 2, 6, 8, 9, 20}...).SumInt64Statistics() 740 | println(res1.GetSum()) 741 | println(res1.GetCount()) 742 | println(res1.GetMax()) 743 | println(res1.GetMin()) 744 | println(res1.GetAverage()) 745 | 746 | res2 := Of([]int32{1, 2, 6, 8, 9, 20}...).SumInt32Statistics() 747 | println(res2.GetSum()) 748 | 749 | res3 := Of([]int{1, 2, 6, 8, 9, 20}...).SumIntStatistics() 750 | println(res3.GetSum()) 751 | 752 | res4 := Of([]float32{1, 2, 6, 8, 9, 20}...).SumFloat32Statistics() 753 | println(res4.GetSum()) 754 | 755 | res5 := Of([]float32{1, 2, 6, 8, 9, 20}...).SumFloat64Statistics() 756 | println(res5.GetSum()) 757 | } 758 | 759 | type Student struct { 760 | Num int 761 | Score int 762 | Age int 763 | Name string 764 | } 765 | 766 | // 班级有一组学号{1,2,3,....,12},对应12个人的信息在内存里面存着 767 | // 把这学号转换成具体的 Student 类,过滤掉 Score 为 1的,并且按评分 Score 分组,并且对各组按照 Age 降序排列 768 | func TestStudents(t *testing.T) { 769 | studentMap := map[int]Student{ 770 | 1: {Num: 1, Name: "小明", Score: 3, Age: 26}, 771 | 2: {Num: 2, Name: "小红", Score: 4, Age: 27}, 772 | 3: {Num: 3, Name: "小李", Score: 5, Age: 19}, 773 | 4: {Num: 4, Name: "老王", Score: 1, Age: 23}, 774 | 5: {Num: 5, Name: "小王", Score: 2, Age: 29}, 775 | 6: {Num: 6, Name: "小绿", Score: 2, Age: 24}, 776 | 7: {Num: 7, Name: "小蓝", Score: 3, Age: 29}, 777 | 8: {Num: 8, Name: "小橙", Score: 3, Age: 30}, 778 | 9: {Num: 9, Name: "小黄", Score: 4, Age: 22}, 779 | 10: {Num: 10, Name: "小黑", Score: 5, Age: 21}, 780 | 11: {Num: 11, Name: "小紫", Score: 3, Age: 32}, 781 | 12: {Num: 12, Name: "小刘", Score: 2, Age: 35}, 782 | } 783 | // v1.0.* 的代码这样实现 此方法已经新 784 | //res := Of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).Map(func(n int) any { 785 | // return studentMap[n] 786 | //}).Filter(func(s any) bool { 787 | // // 这里需要强转 788 | // tempS := s.(Student) 789 | // // 过滤掉1的 790 | // return tempS.Score != 1 791 | //}).Collect(collectors.GroupingBy(func(t any) int { 792 | // return t.(Student).Score 793 | //}, func(t any) any { 794 | // return t 795 | //}, func(t1 []any) { 796 | // sort.Slice(t1, func(i, j int) bool { 797 | // return t1[i].(Student).Age < t1[j].(Student).Age 798 | // }) 799 | //})) 800 | //println(res) 801 | 802 | // v1.1.* 的代码这样实现 803 | res := GroupingBy(Map(Of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), func(n int) Student { 804 | // 注意 这里的返回类型可以是目标类型了 805 | return studentMap[n] 806 | }).Filter(func(s Student) bool { 807 | // 这里过滤也不需要转换类型 808 | // 过滤掉1的 809 | return s.Score != 1 810 | }), func(t Student) int { 811 | return t.Score 812 | }, func(t Student) Student { 813 | return t 814 | }, func(t1 []Student) { 815 | // 按年龄降序排列 816 | sort.Slice(t1, func(i, j int) bool { 817 | return t1[i].Age > t1[j].Age 818 | }) 819 | }) 820 | println(res) 821 | } 822 | 823 | func TestStream_TakeWhile(t *testing.T) { 824 | takeRes := Of( 825 | TestItem{itemNum: 1, itemValue: "item1"}, 826 | TestItem{itemNum: 2, itemValue: "3tem21"}, 827 | TestItem{itemNum: 2, itemValue: "2tem22"}, 828 | TestItem{itemNum: 2, itemValue: "1tem22"}, 829 | TestItem{itemNum: 2, itemValue: "4tem23"}, 830 | TestItem{itemNum: 3, itemValue: "item3"}, 831 | ).TakeWhile(func(s TestItem) bool { 832 | return s.itemNum == 2 833 | }).ToSlice() 834 | println(takeRes) 835 | } 836 | 837 | func TestStream_DropWhile(t *testing.T) { 838 | dropRes := Of( 839 | TestItem{itemNum: 1, itemValue: "item1"}, 840 | TestItem{itemNum: 2, itemValue: "3tem21"}, 841 | TestItem{itemNum: 2, itemValue: "2tem22"}, 842 | TestItem{itemNum: 2, itemValue: "1tem22"}, 843 | TestItem{itemNum: 2, itemValue: "4tem23"}, 844 | TestItem{itemNum: 3, itemValue: "item3"}, 845 | ).DropWhile(func(s TestItem) bool { 846 | return s.itemNum == 2 847 | }).ToSlice() 848 | println(dropRes) 849 | } 850 | 851 | func TestStream_Optional(t *testing.T) { 852 | t1 := &TestItem{itemNum: 1, itemValue: "item1"} 853 | tOpt := OfOptional[TestItem](t1) 854 | tOpt.IsPresent() 855 | tOpt.OrElseGet(func() TestItem { 856 | return TestItem{itemNum: 2, itemValue: "3tem21"} 857 | }) 858 | tOpt.IfPresent(func(o TestItem) { 859 | println(o.itemValue) 860 | }) 861 | if v, ok := tOpt.Get(); ok { 862 | println(v.itemValue) 863 | } 864 | nilOpt := OfOptionalNilable[TestItem](nil) 865 | if v, ok := nilOpt.Get(); ok { 866 | println(v.itemValue) 867 | } 868 | te := nilOpt.OrElse(TestItem{itemNum: 3, itemValue: "3tem31"}) 869 | on := OfOptional(&te) 870 | if v, ok := on.Get(); ok { 871 | println(v.itemValue) 872 | } 873 | println("end") 874 | } 875 | -------------------------------------------------------------------------------- /utils/common.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func If[T any](trueCondition bool, v1, v2 T) T { 4 | if trueCondition { 5 | return v1 6 | } 7 | return v2 8 | } 9 | -------------------------------------------------------------------------------- /utils/conv.go: -------------------------------------------------------------------------------- 1 | // See https://github.com/dablelv/cyan/blob/master/conv/conv.go 2 | package utils 3 | 4 | import ( 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "html/template" 9 | "reflect" 10 | "strconv" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | var errNegativeNotAllowed = errors.New("unable to cast negative value") 16 | 17 | var ( 18 | errorType = reflect.TypeOf((*error)(nil)).Elem() 19 | fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() 20 | ) 21 | 22 | // ToAny converts one type to another type. 23 | func ToAny[T any](a any) T { 24 | v, _ := ToAnyE[T](a) 25 | return v 26 | } 27 | 28 | // ToAnyE converts one type to another and returns an error if error occurred. 29 | func ToAnyE[T any](a any) (T, error) { 30 | var t T 31 | switch any(t).(type) { 32 | case bool: 33 | v, err := ToBoolE(a) 34 | if err != nil { 35 | return t, err 36 | } 37 | t = any(v).(T) 38 | case int: 39 | v, err := ToIntE(a) 40 | if err != nil { 41 | return t, err 42 | } 43 | t = any(v).(T) 44 | case int8: 45 | v, err := ToInt8E(a) 46 | if err != nil { 47 | return t, err 48 | } 49 | t = any(v).(T) 50 | case int16: 51 | v, err := ToInt16E(a) 52 | if err != nil { 53 | return t, err 54 | } 55 | t = any(v).(T) 56 | case int32: 57 | v, err := ToInt32E(a) 58 | if err != nil { 59 | return t, err 60 | } 61 | t = any(v).(T) 62 | case int64: 63 | v, err := ToInt64E(a) 64 | if err != nil { 65 | return t, err 66 | } 67 | t = any(v).(T) 68 | case uint: 69 | v, err := ToUintE(a) 70 | if err != nil { 71 | return t, err 72 | } 73 | t = any(v).(T) 74 | case uint8: 75 | v, err := ToUint8E(a) 76 | if err != nil { 77 | return t, err 78 | } 79 | t = any(v).(T) 80 | case uint16: 81 | v, err := ToUint16E(a) 82 | if err != nil { 83 | return t, err 84 | } 85 | t = any(v).(T) 86 | case uint32: 87 | v, err := ToUint32E(a) 88 | if err != nil { 89 | return t, err 90 | } 91 | t = any(v).(T) 92 | case uint64: 93 | v, err := ToUint64E(a) 94 | if err != nil { 95 | return t, err 96 | } 97 | t = any(v).(T) 98 | case float32: 99 | v, err := ToFloat32E(a) 100 | if err != nil { 101 | return t, err 102 | } 103 | t = any(v).(T) 104 | case float64: 105 | v, err := ToFloat64E(a) 106 | if err != nil { 107 | return t, err 108 | } 109 | t = any(v).(T) 110 | case time.Duration: 111 | v, err := ToDurationE(a) 112 | if err != nil { 113 | return t, err 114 | } 115 | t = any(v).(T) 116 | case string: 117 | v, err := ToStringE(a) 118 | if err != nil { 119 | return t, err 120 | } 121 | t = any(v).(T) 122 | default: 123 | return t, fmt.Errorf("The type %T isn't supported", t) 124 | } 125 | return t, nil 126 | } 127 | 128 | // ToBoolE casts any type to a bool type. 129 | func ToBoolE(a any) (bool, error) { 130 | a = indirect(a) 131 | 132 | switch b := a.(type) { 133 | case bool: 134 | return b, nil 135 | case nil: 136 | return false, nil 137 | case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8, float64, float32, uintptr, complex64, complex128: 138 | return !reflect.ValueOf(a).IsZero(), nil 139 | case string: 140 | return strconv.ParseBool(a.(string)) 141 | case time.Duration: 142 | return b != 0, nil 143 | case json.Number: 144 | v, err := b.Float64() 145 | return v != 0, err 146 | default: 147 | return false, fmt.Errorf("unable to cast %#v of type %T to bool", a, a) 148 | } 149 | } 150 | 151 | // ToIntE casts any type to an int type. 152 | func ToIntE(i any) (int, error) { 153 | i = indirect(i) 154 | 155 | intv, ok := toInt(i) 156 | if ok { 157 | return intv, nil 158 | } 159 | 160 | switch s := i.(type) { 161 | case bool: 162 | if s { 163 | return 1, nil 164 | } 165 | return 0, nil 166 | case int64: 167 | return int(s), nil 168 | case int32: 169 | return int(s), nil 170 | case int16: 171 | return int(s), nil 172 | case int8: 173 | return int(s), nil 174 | case uint: 175 | return int(s), nil 176 | case uint64: 177 | return int(s), nil 178 | case uint32: 179 | return int(s), nil 180 | case uint16: 181 | return int(s), nil 182 | case uint8: 183 | return int(s), nil 184 | case float64: 185 | return int(s), nil 186 | case float32: 187 | return int(s), nil 188 | case string: 189 | v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) 190 | if err == nil { 191 | return int(v), nil 192 | } 193 | return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i) 194 | case json.Number: 195 | v, err := s.Int64() 196 | return int(v), err 197 | case nil: 198 | return 0, nil 199 | default: 200 | return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i) 201 | } 202 | } 203 | 204 | // ToInt8E casts any type to an int8 type. 205 | func ToInt8E(i any) (int8, error) { 206 | i = indirect(i) 207 | 208 | intv, ok := toInt(i) 209 | if ok { 210 | return int8(intv), nil 211 | } 212 | 213 | switch s := i.(type) { 214 | case bool: 215 | if s { 216 | return 1, nil 217 | } 218 | return 0, nil 219 | case int64: 220 | return int8(s), nil 221 | case int32: 222 | return int8(s), nil 223 | case int16: 224 | return int8(s), nil 225 | case int8: 226 | return s, nil 227 | case uint: 228 | return int8(s), nil 229 | case uint64: 230 | return int8(s), nil 231 | case uint32: 232 | return int8(s), nil 233 | case uint16: 234 | return int8(s), nil 235 | case uint8: 236 | return int8(s), nil 237 | case float64: 238 | return int8(s), nil 239 | case float32: 240 | return int8(s), nil 241 | case string: 242 | v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) 243 | if err == nil { 244 | return int8(v), nil 245 | } 246 | return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i) 247 | case json.Number: 248 | v, err := s.Int64() 249 | return int8(v), err 250 | case nil: 251 | return 0, nil 252 | default: 253 | return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i) 254 | } 255 | } 256 | 257 | // ToInt16E casts any type to an int16 type. 258 | func ToInt16E(i any) (int16, error) { 259 | i = indirect(i) 260 | 261 | intv, ok := toInt(i) 262 | if ok { 263 | return int16(intv), nil 264 | } 265 | 266 | switch s := i.(type) { 267 | case bool: 268 | if s { 269 | return 1, nil 270 | } 271 | return 0, nil 272 | case int64: 273 | return int16(s), nil 274 | case int32: 275 | return int16(s), nil 276 | case int16: 277 | return s, nil 278 | case int8: 279 | return int16(s), nil 280 | case uint: 281 | return int16(s), nil 282 | case uint64: 283 | return int16(s), nil 284 | case uint32: 285 | return int16(s), nil 286 | case uint16: 287 | return int16(s), nil 288 | case uint8: 289 | return int16(s), nil 290 | case float64: 291 | return int16(s), nil 292 | case float32: 293 | return int16(s), nil 294 | case string: 295 | v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) 296 | if err == nil { 297 | return int16(v), nil 298 | } 299 | return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i) 300 | case json.Number: 301 | v, err := s.Int64() 302 | return int16(v), err 303 | case nil: 304 | return 0, nil 305 | default: 306 | return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i) 307 | } 308 | } 309 | 310 | // ToInt32E casts any type to an int32 type. 311 | func ToInt32E(i any) (int32, error) { 312 | i = indirect(i) 313 | 314 | intv, ok := toInt(i) 315 | if ok { 316 | return int32(intv), nil 317 | } 318 | 319 | switch s := i.(type) { 320 | case int64: 321 | return int32(s), nil 322 | case int32: 323 | return s, nil 324 | case int16: 325 | return int32(s), nil 326 | case int8: 327 | return int32(s), nil 328 | case uint: 329 | return int32(s), nil 330 | case uint64: 331 | return int32(s), nil 332 | case uint32: 333 | return int32(s), nil 334 | case uint16: 335 | return int32(s), nil 336 | case uint8: 337 | return int32(s), nil 338 | case float64: 339 | return int32(s), nil 340 | case float32: 341 | return int32(s), nil 342 | case string: 343 | v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) 344 | if err == nil { 345 | return int32(v), nil 346 | } 347 | return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i) 348 | case json.Number: 349 | v, err := s.Int64() 350 | return int32(v), err 351 | case bool: 352 | if s { 353 | return 1, nil 354 | } 355 | return 0, nil 356 | case nil: 357 | return 0, nil 358 | default: 359 | return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i) 360 | } 361 | } 362 | 363 | // ToInt64E casts any to an int64 type. 364 | func ToInt64E(i any) (int64, error) { 365 | i = indirect(i) 366 | 367 | intv, ok := toInt(i) 368 | if ok { 369 | return int64(intv), nil 370 | } 371 | 372 | switch s := i.(type) { 373 | case bool: 374 | if s { 375 | return 1, nil 376 | } 377 | return 0, nil 378 | case int64: 379 | return s, nil 380 | case int32: 381 | return int64(s), nil 382 | case int16: 383 | return int64(s), nil 384 | case int8: 385 | return int64(s), nil 386 | case uint: 387 | return int64(s), nil 388 | case uint64: 389 | return int64(s), nil 390 | case uint32: 391 | return int64(s), nil 392 | case uint16: 393 | return int64(s), nil 394 | case uint8: 395 | return int64(s), nil 396 | case float64: 397 | return int64(s), nil 398 | case float32: 399 | return int64(s), nil 400 | case string: 401 | v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) 402 | if err == nil { 403 | return v, nil 404 | } 405 | return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i) 406 | case json.Number: 407 | return s.Int64() 408 | case nil: 409 | return 0, nil 410 | default: 411 | return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i) 412 | } 413 | } 414 | 415 | // ToUintE casts any type to a uint type. 416 | func ToUintE(i any) (uint, error) { 417 | i = indirect(i) 418 | 419 | intv, ok := toInt(i) 420 | if ok { 421 | if intv < 0 { 422 | return 0, errNegativeNotAllowed 423 | } 424 | return uint(intv), nil 425 | } 426 | 427 | switch s := i.(type) { 428 | case bool: 429 | if s { 430 | return 1, nil 431 | } 432 | return 0, nil 433 | case int64: 434 | if s < 0 { 435 | return 0, errNegativeNotAllowed 436 | } 437 | return uint(s), nil 438 | case int32: 439 | if s < 0 { 440 | return 0, errNegativeNotAllowed 441 | } 442 | return uint(s), nil 443 | case int16: 444 | if s < 0 { 445 | return 0, errNegativeNotAllowed 446 | } 447 | return uint(s), nil 448 | case int8: 449 | if s < 0 { 450 | return 0, errNegativeNotAllowed 451 | } 452 | return uint(s), nil 453 | case uint: 454 | return s, nil 455 | case uint64: 456 | return uint(s), nil 457 | case uint32: 458 | return uint(s), nil 459 | case uint16: 460 | return uint(s), nil 461 | case uint8: 462 | return uint(s), nil 463 | case float64: 464 | if s < 0 { 465 | return 0, errNegativeNotAllowed 466 | } 467 | return uint(s), nil 468 | case float32: 469 | if s < 0 { 470 | return 0, errNegativeNotAllowed 471 | } 472 | return uint(s), nil 473 | case string: 474 | v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) 475 | if v < 0 { 476 | return 0, errNegativeNotAllowed 477 | } 478 | return uint(v), err 479 | case json.Number: 480 | v, err := s.Int64() 481 | if v < 0 { 482 | return 0, errNegativeNotAllowed 483 | } 484 | return uint(v), err 485 | case nil: 486 | return 0, nil 487 | default: 488 | return 0, fmt.Errorf("unable to cast %#v of type %T to uint", i, i) 489 | } 490 | } 491 | 492 | // ToUint8E casts any type to a uint type. 493 | func ToUint8E(i any) (uint8, error) { 494 | i = indirect(i) 495 | 496 | intv, ok := toInt(i) 497 | if ok { 498 | if intv < 0 { 499 | return 0, errNegativeNotAllowed 500 | } 501 | return uint8(intv), nil 502 | } 503 | 504 | switch s := i.(type) { 505 | case bool: 506 | if s { 507 | return 1, nil 508 | } 509 | return 0, nil 510 | case int64: 511 | if s < 0 { 512 | return 0, errNegativeNotAllowed 513 | } 514 | return uint8(s), nil 515 | case int32: 516 | if s < 0 { 517 | return 0, errNegativeNotAllowed 518 | } 519 | return uint8(s), nil 520 | case int16: 521 | if s < 0 { 522 | return 0, errNegativeNotAllowed 523 | } 524 | return uint8(s), nil 525 | case int8: 526 | if s < 0 { 527 | return 0, errNegativeNotAllowed 528 | } 529 | return uint8(s), nil 530 | case uint: 531 | return uint8(s), nil 532 | case uint64: 533 | return uint8(s), nil 534 | case uint32: 535 | return uint8(s), nil 536 | case uint16: 537 | return uint8(s), nil 538 | case uint8: 539 | return s, nil 540 | case float64: 541 | if s < 0 { 542 | return 0, errNegativeNotAllowed 543 | } 544 | return uint8(s), nil 545 | case float32: 546 | if s < 0 { 547 | return 0, errNegativeNotAllowed 548 | } 549 | return uint8(s), nil 550 | case string: 551 | v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) 552 | if v < 0 { 553 | return 0, errNegativeNotAllowed 554 | } 555 | return uint8(v), err 556 | case json.Number: 557 | v, err := s.Int64() 558 | if v < 0 { 559 | return 0, errNegativeNotAllowed 560 | } 561 | return uint8(v), err 562 | case nil: 563 | return 0, nil 564 | default: 565 | return 0, fmt.Errorf("unable to cast %#v of type %T to uint8", i, i) 566 | } 567 | } 568 | 569 | // ToUint16E casts any type to a uint16 type. 570 | func ToUint16E(i any) (uint16, error) { 571 | i = indirect(i) 572 | 573 | intv, ok := toInt(i) 574 | if ok { 575 | if intv < 0 { 576 | return 0, errNegativeNotAllowed 577 | } 578 | return uint16(intv), nil 579 | } 580 | 581 | switch s := i.(type) { 582 | case bool: 583 | if s { 584 | return 1, nil 585 | } 586 | return 0, nil 587 | case int64: 588 | if s < 0 { 589 | return 0, errNegativeNotAllowed 590 | } 591 | return uint16(s), nil 592 | case int32: 593 | if s < 0 { 594 | return 0, errNegativeNotAllowed 595 | } 596 | return uint16(s), nil 597 | case int16: 598 | if s < 0 { 599 | return 0, errNegativeNotAllowed 600 | } 601 | return uint16(s), nil 602 | case int8: 603 | if s < 0 { 604 | return 0, errNegativeNotAllowed 605 | } 606 | return uint16(s), nil 607 | case uint: 608 | return uint16(s), nil 609 | case uint64: 610 | return uint16(s), nil 611 | case uint32: 612 | return uint16(s), nil 613 | case uint16: 614 | return s, nil 615 | case uint8: 616 | return uint16(s), nil 617 | case float64: 618 | if s < 0 { 619 | return 0, errNegativeNotAllowed 620 | } 621 | return uint16(s), nil 622 | case float32: 623 | if s < 0 { 624 | return 0, errNegativeNotAllowed 625 | } 626 | return uint16(s), nil 627 | case string: 628 | v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) 629 | if v < 0 { 630 | return 0, errNegativeNotAllowed 631 | } 632 | return uint16(v), err 633 | case json.Number: 634 | v, err := s.Int64() 635 | if v < 0 { 636 | return 0, errNegativeNotAllowed 637 | } 638 | return uint16(v), err 639 | case nil: 640 | return 0, nil 641 | default: 642 | return 0, fmt.Errorf("unable to cast %#v of type %T to uint16", i, i) 643 | } 644 | } 645 | 646 | // ToUint32E casts any type to a uint32 type. 647 | func ToUint32E(i any) (uint32, error) { 648 | i = indirect(i) 649 | 650 | intv, ok := toInt(i) 651 | if ok { 652 | if intv < 0 { 653 | return 0, errNegativeNotAllowed 654 | } 655 | return uint32(intv), nil 656 | } 657 | 658 | switch s := i.(type) { 659 | case bool: 660 | if s { 661 | return 1, nil 662 | } 663 | return 0, nil 664 | case int64: 665 | if s < 0 { 666 | return 0, errNegativeNotAllowed 667 | } 668 | return uint32(s), nil 669 | case int32: 670 | if s < 0 { 671 | return 0, errNegativeNotAllowed 672 | } 673 | return uint32(s), nil 674 | case int16: 675 | if s < 0 { 676 | return 0, errNegativeNotAllowed 677 | } 678 | return uint32(s), nil 679 | case int8: 680 | if s < 0 { 681 | return 0, errNegativeNotAllowed 682 | } 683 | return uint32(s), nil 684 | case uint: 685 | return uint32(s), nil 686 | case uint64: 687 | return uint32(s), nil 688 | case uint32: 689 | return s, nil 690 | case uint16: 691 | return uint32(s), nil 692 | case uint8: 693 | return uint32(s), nil 694 | case float64: 695 | if s < 0 { 696 | return 0, errNegativeNotAllowed 697 | } 698 | return uint32(s), nil 699 | case float32: 700 | if s < 0 { 701 | return 0, errNegativeNotAllowed 702 | } 703 | return uint32(s), nil 704 | case string: 705 | v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) 706 | if v < 0 { 707 | return 0, errNegativeNotAllowed 708 | } 709 | return uint32(v), err 710 | case json.Number: 711 | v, err := s.Int64() 712 | if v < 0 { 713 | return 0, errNegativeNotAllowed 714 | } 715 | return uint32(v), err 716 | case nil: 717 | return 0, nil 718 | default: 719 | return 0, fmt.Errorf("unable to cast %#v of type %T to uint32", i, i) 720 | } 721 | } 722 | 723 | // ToUint64E casts any type to a uint64 type. 724 | func ToUint64E(i any) (uint64, error) { 725 | i = indirect(i) 726 | 727 | intv, ok := toInt(i) 728 | if ok { 729 | if intv < 0 { 730 | return 0, errNegativeNotAllowed 731 | } 732 | return uint64(intv), nil 733 | } 734 | 735 | switch s := i.(type) { 736 | case bool: 737 | if s { 738 | return 1, nil 739 | } 740 | return 0, nil 741 | case int64: 742 | if s < 0 { 743 | return 0, errNegativeNotAllowed 744 | } 745 | return uint64(s), nil 746 | case int32: 747 | if s < 0 { 748 | return 0, errNegativeNotAllowed 749 | } 750 | return uint64(s), nil 751 | case int16: 752 | if s < 0 { 753 | return 0, errNegativeNotAllowed 754 | } 755 | return uint64(s), nil 756 | case int8: 757 | if s < 0 { 758 | return 0, errNegativeNotAllowed 759 | } 760 | return uint64(s), nil 761 | case uint: 762 | return uint64(s), nil 763 | case uint64: 764 | return s, nil 765 | case uint32: 766 | return uint64(s), nil 767 | case uint16: 768 | return uint64(s), nil 769 | case uint8: 770 | return uint64(s), nil 771 | case float32: 772 | if s < 0 { 773 | return 0, errNegativeNotAllowed 774 | } 775 | return uint64(s), nil 776 | case float64: 777 | if s < 0 { 778 | return 0, errNegativeNotAllowed 779 | } 780 | return uint64(s), nil 781 | case string: 782 | v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) 783 | if v < 0 { 784 | return 0, errNegativeNotAllowed 785 | } 786 | return uint64(v), err 787 | case json.Number: 788 | v, err := s.Int64() 789 | if v < 0 { 790 | return 0, errNegativeNotAllowed 791 | } 792 | return uint64(v), err 793 | case nil: 794 | return 0, nil 795 | default: 796 | return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i) 797 | } 798 | } 799 | 800 | // ToFloat32E casts any type to a float32 type. 801 | func ToFloat32E(i any) (float32, error) { 802 | i = indirect(i) 803 | 804 | intv, ok := toInt(i) 805 | if ok { 806 | return float32(intv), nil 807 | } 808 | 809 | switch s := i.(type) { 810 | case float64: 811 | return float32(s), nil 812 | case float32: 813 | return s, nil 814 | case int64: 815 | return float32(s), nil 816 | case int32: 817 | return float32(s), nil 818 | case int16: 819 | return float32(s), nil 820 | case int8: 821 | return float32(s), nil 822 | case uint: 823 | return float32(s), nil 824 | case uint64: 825 | return float32(s), nil 826 | case uint32: 827 | return float32(s), nil 828 | case uint16: 829 | return float32(s), nil 830 | case uint8: 831 | return float32(s), nil 832 | case string: 833 | v, err := strconv.ParseFloat(s, 32) 834 | return float32(v), err 835 | case json.Number: 836 | v, err := s.Float64() 837 | return float32(v), err 838 | case bool: 839 | if s { 840 | return 1, nil 841 | } 842 | return 0, nil 843 | case nil: 844 | return 0, nil 845 | default: 846 | return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i) 847 | } 848 | } 849 | 850 | // ToFloat64E casts any type to a float64 type. 851 | func ToFloat64E(i any) (float64, error) { 852 | i = indirect(i) 853 | 854 | intv, ok := toInt(i) 855 | if ok { 856 | return float64(intv), nil 857 | } 858 | 859 | switch s := i.(type) { 860 | case float64: 861 | return s, nil 862 | case float32: 863 | return float64(s), nil 864 | case int64: 865 | return float64(s), nil 866 | case int32: 867 | return float64(s), nil 868 | case int16: 869 | return float64(s), nil 870 | case int8: 871 | return float64(s), nil 872 | case uint: 873 | return float64(s), nil 874 | case uint64: 875 | return float64(s), nil 876 | case uint32: 877 | return float64(s), nil 878 | case uint16: 879 | return float64(s), nil 880 | case uint8: 881 | return float64(s), nil 882 | case string: 883 | return strconv.ParseFloat(s, 64) 884 | case json.Number: 885 | return s.Float64() 886 | case bool: 887 | if s { 888 | return 1, nil 889 | } 890 | return 0, nil 891 | case nil: 892 | return 0, nil 893 | default: 894 | return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i) 895 | } 896 | } 897 | 898 | // ToStringE casts any type to a string type. 899 | func ToStringE(i any) (string, error) { 900 | i = indirectToStringerOrError(i) 901 | 902 | switch s := i.(type) { 903 | case string: 904 | return s, nil 905 | case bool: 906 | return strconv.FormatBool(s), nil 907 | case int: 908 | return strconv.Itoa(s), nil 909 | case int64: 910 | return strconv.FormatInt(s, 10), nil 911 | case int32: 912 | return strconv.Itoa(int(s)), nil 913 | case int16: 914 | return strconv.FormatInt(int64(s), 10), nil 915 | case int8: 916 | return strconv.FormatInt(int64(s), 10), nil 917 | case uint: 918 | return strconv.FormatUint(uint64(s), 10), nil 919 | case uint64: 920 | return strconv.FormatUint(uint64(s), 10), nil 921 | case uint32: 922 | return strconv.FormatUint(uint64(s), 10), nil 923 | case uint16: 924 | return strconv.FormatUint(uint64(s), 10), nil 925 | case uint8: 926 | return strconv.FormatUint(uint64(s), 10), nil 927 | case float64: 928 | return strconv.FormatFloat(s, 'f', -1, 64), nil 929 | case float32: 930 | return strconv.FormatFloat(float64(s), 'f', -1, 32), nil 931 | case json.Number: 932 | return s.String(), nil 933 | case []byte: 934 | return string(s), nil 935 | case template.HTML: 936 | return string(s), nil 937 | case template.HTMLAttr: 938 | return string(s), nil 939 | case template.URL: 940 | return string(s), nil 941 | case template.JS: 942 | return string(s), nil 943 | case template.JSStr: 944 | return string(s), nil 945 | case template.CSS: 946 | return string(s), nil 947 | case template.Srcset: 948 | return string(s), nil 949 | case nil: 950 | return "", nil 951 | case fmt.Stringer: 952 | return s.String(), nil 953 | case error: 954 | return s.Error(), nil 955 | default: 956 | return "", fmt.Errorf("Unable to cast %#v of type %T to string", i, i) 957 | } 958 | } 959 | 960 | // ToDurationE casts any type to time.Duration type. 961 | func ToDurationE(i any) (time.Duration, error) { 962 | i = indirect(i) 963 | 964 | switch s := i.(type) { 965 | case time.Duration: 966 | return s, nil 967 | case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8: 968 | return time.Duration(ToAny[int64](s)), nil 969 | case float32, float64: 970 | return time.Duration(ToAny[float64](s)), nil 971 | case string: 972 | if strings.ContainsAny(s, "nsuµmh") { 973 | return time.ParseDuration(s) 974 | } 975 | return time.ParseDuration(s + "ns") 976 | case json.Number: 977 | v, err := s.Float64() 978 | return time.Duration(v), err 979 | default: 980 | return time.Duration(0), fmt.Errorf("unable to cast %#v of type %T to Duration", i, i) 981 | } 982 | } 983 | 984 | // toInt returns the int value of v if v or v's underlying type is an int. 985 | // Note that this will return false for int64 etc. types. 986 | func toInt(v any) (int, bool) { 987 | switch v := v.(type) { 988 | case int: 989 | return v, true 990 | case time.Weekday: 991 | return int(v), true 992 | case time.Month: 993 | return int(v), true 994 | default: 995 | return 0, false 996 | } 997 | } 998 | 999 | // Copied from html/template/content.go. 1000 | // indirect returns the value, after dereferencing as many times 1001 | // as necessary to reach the base type (or nil). 1002 | func indirect(a any) any { 1003 | if a == nil { 1004 | return nil 1005 | } 1006 | if t := reflect.TypeOf(a); t.Kind() != reflect.Pointer { 1007 | // Avoid creating a reflect.Value if it's not a pointer. 1008 | return a 1009 | } 1010 | v := reflect.ValueOf(a) 1011 | for v.Kind() == reflect.Pointer && !v.IsNil() { 1012 | v = v.Elem() 1013 | } 1014 | return v.Interface() 1015 | } 1016 | 1017 | // Copied from html/template/content.go. 1018 | // indirectToStringerOrError returns the value, after dereferencing as many times 1019 | // as necessary to reach the base type (or nil) or an implementation of fmt.Stringer 1020 | // or error. 1021 | func indirectToStringerOrError(a any) any { 1022 | if a == nil { 1023 | return nil 1024 | } 1025 | v := reflect.ValueOf(a) 1026 | for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Pointer && !v.IsNil() { 1027 | v = v.Elem() 1028 | } 1029 | return v.Interface() 1030 | } 1031 | 1032 | // trimZeroDecimal trims the zero decimal. 1033 | // E.g. 12.00 to 12 while 12.01 still to be 12.01. 1034 | func trimZeroDecimal(s string) string { 1035 | var foundZero bool 1036 | for i := len(s); i > 0; i-- { 1037 | switch s[i-1] { 1038 | case '.': 1039 | if foundZero { 1040 | return s[:i-1] 1041 | } 1042 | case '0': 1043 | foundZero = true 1044 | default: 1045 | return s 1046 | } 1047 | } 1048 | return s 1049 | } 1050 | --------------------------------------------------------------------------------