├── .gitignore ├── LICENSE ├── README.md ├── benchmark ├── README.md └── benchmark_test.go ├── circle.yml ├── distinct.go ├── distinct_test.go ├── example_test.go ├── examples ├── custom_type │ └── main.go ├── distinct │ └── main.go ├── filter │ └── main.go ├── flatMap │ └── main.go ├── flatten │ └── main.go ├── fold │ └── main.go ├── map │ └── main.go ├── reduce │ └── main.go ├── skip │ └── main.go ├── sort │ └── main.go ├── stream │ ├── distinct │ │ └── main.go │ ├── filter │ │ └── main.go │ ├── flatMap │ │ └── main.go │ ├── flatten │ │ └── main.go │ ├── fold │ │ └── main.go │ ├── map │ │ └── main.go │ ├── reduce │ │ └── main.go │ ├── sort │ │ └── main.go │ └── take │ │ └── main.go └── take │ └── main.go ├── filter.go ├── filter_test.go ├── flat_map.go ├── flat_map_test.go ├── flatten.go ├── flatten_test.go ├── fold.go ├── fold_test.go ├── gollection.go ├── gollection_test.go ├── map.go ├── map_test.go ├── process.go ├── reduce.go ├── reduce_test.go ├── skip.go ├── skip_test.go ├── sort.go ├── sort_test.go ├── take.go ├── take_test.go └── validate.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | *.swp 27 | .idea 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Naoya Yoshizawa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gollection 2 | [![GoDoc](https://godoc.org/gopkg.in/azihsoyn/gollection.v1?status.svg)](https://godoc.org/gopkg.in/azihsoyn/gollection.v1) 3 | [![Coverage Status](https://coveralls.io/repos/github/azihsoyn/gollection/badge.svg?branch=master)](https://coveralls.io/github/azihsoyn/gollection?branch=master) 4 | [![CircleCI](https://circleci.com/gh/azihsoyn/gollection.svg?style=svg)](https://circleci.com/gh/azihsoyn/gollection) 5 | 6 | gollection is golang collection util library (inspired by [kotlin collection](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/index.html)) 7 | 8 | # supported functions 9 | - distinct 10 | - distinctBy 11 | - filter 12 | - flatMap 13 | - flatten 14 | - fold 15 | - map 16 | - reduce 17 | - skip ([java8](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#skip-long-)) 18 | - sortBy 19 | - take 20 | 21 | # Feature 22 | 23 | - read-only original slice 24 | - stream(goroutine) support 25 | 26 | # Installation 27 | 28 | `go get github.com/azihsoyn/gollection` or `go get gopkg.in/azihsoyn/gollection.v1` 29 | 30 | # Usage 31 | see [examples code](https://github.com/azihsoyn/gollection/tree/master/examples) 32 | 33 | or [godoc](https://coveralls.io/github/azihsoyn/gollection?branch=master) [examples](https://godoc.org/github.com/azihsoyn/gollection#pkg-examples) 34 | 35 | # LICENSE 36 | MIT 37 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | # Benchmark 2 | 3 | ``` 4 | $ go test -bench . -benchmem 5 | BenchmarkNew-8 20000000 99.5 ns/op 96 B/op 2 allocs/op 6 | BenchmarkNewStream-8 200000 10247 ns/op 289 B/op 8 allocs/op 7 | BenchmarkDistinct-8 300000 4267 ns/op 1155 B/op 36 allocs/op 8 | BenchmarkDistinct_Stream-8 5000 265562 ns/op 1717 B/op 49 allocs/op 9 | BenchmarkDistinct_WithoutGollection-8 1000000 1211 ns/op 339 B/op 2 allocs/op 10 | BenchmarkDistinctBy-8 200000 9064 ns/op 1955 B/op 56 allocs/op 11 | BenchmarkDistinctBy_Stream-8 5000 296762 ns/op 2678 B/op 89 allocs/op 12 | BenchmarkDistinctBy_WithoutGollection-8 1000000 1267 ns/op 339 B/op 2 allocs/op 13 | BenchmarkFilter-8 200000 7145 ns/op 1568 B/op 53 allocs/op 14 | BenchmarkFilter_Stream-8 10000 157181 ns/op 1952 B/op 83 allocs/op 15 | BenchmarkFilter_WithoutGollection-8 20000000 84.8 ns/op 160 B/op 1 allocs/op 16 | BenchmarkFlatMap-8 200000 8498 ns/op 2256 B/op 71 allocs/op 17 | BenchmarkFlatMap_Stream-8 10000 144181 ns/op 2656 B/op 80 allocs/op 18 | BenchmarkFlatMap_WithoutGollection-8 5000000 240 ns/op 368 B/op 4 allocs/op 19 | BenchmarkFlatten-8 500000 2990 ns/op 1296 B/op 31 allocs/op 20 | BenchmarkFlatten_Stream-8 10000 121338 ns/op 1856 B/op 60 allocs/op 21 | BenchmarkFlatten_WithoutGollection-8 10000000 232 ns/op 368 B/op 4 allocs/op 22 | BenchmarkFold-8 200000 6847 ns/op 1448 B/op 44 allocs/op 23 | BenchmarkFold_Stream-8 10000 100037 ns/op 1744 B/op 68 allocs/op 24 | BenchmarkFold_WithoutGollection-8 100000000 10.8 ns/op 0 B/op 0 allocs/op 25 | BenchmarkMap-8 200000 7645 ns/op 1952 B/op 65 allocs/op 26 | BenchmarkMap_Stream-8 10000 135785 ns/op 2720 B/op 97 allocs/op 27 | BenchmarkMap_WithoutGollection-8 20000000 83.7 ns/op 160 B/op 1 allocs/op 28 | BenchmarkReduce-8 200000 7254 ns/op 1384 B/op 42 allocs/op 29 | BenchmarkReduce_Stream-8 10000 113624 ns/op 1680 B/op 65 allocs/op 30 | BenchmarkReduce_WithoutGollection-8 100000000 14.6 ns/op 0 B/op 0 allocs/op 31 | BenchmarkSort-8 100000 22518 ns/op 4336 B/op 129 allocs/op 32 | BenchmarkSort_Stream-8 5000 222876 ns/op 7136 B/op 232 allocs/op 33 | BenchmarkSort_WithoutGollection-8 3000000 437 ns/op 32 B/op 1 allocs/op 34 | BenchmarkTake-8 3000000 600 ns/op 448 B/op 8 allocs/op 35 | BenchmarkTake_Stream-8 50000 34518 ns/op 1221 B/op 24 allocs/op 36 | BenchmarkTake_WithoutGollection-8 5000000 229 ns/op 160 B/op 1 allocs/op 37 | PASS 38 | ok github.com/azihsoyn/gollection/benchmark 51.777s 39 | ``` 40 | 41 | machine spec 42 | ``` 43 | $ system_profiler SPHardwareDataType 44 | Hardware: 45 | 46 | Hardware Overview: 47 | 48 | Model Name: MacBook Pro 49 | Model Identifier: MacBookPro11,4 50 | Processor Name: Intel Core i7 51 | Processor Speed: 2.2 GHz 52 | Number of Processors: 1 53 | Total Number of Cores: 4 54 | L2 Cache (per Core): 256 KB 55 | L3 Cache: 6 MB 56 | Memory: 16 GB 57 | ``` 58 | -------------------------------------------------------------------------------- /benchmark/benchmark_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "sort" 5 | "testing" 6 | 7 | "github.com/azihsoyn/gollection" 8 | ) 9 | 10 | func BenchmarkNew(b *testing.B) { 11 | b.ResetTimer() 12 | for i := 0; i < b.N; i++ { 13 | gollection.New([]int{}) 14 | } 15 | } 16 | 17 | func BenchmarkNewAndResult(b *testing.B) { 18 | b.ResetTimer() 19 | for i := 0; i < b.N; i++ { 20 | gollection.New([]int{}).Result() 21 | } 22 | } 23 | 24 | func BenchmarkNewAndResultAs(b *testing.B) { 25 | ret := []int{} 26 | b.ResetTimer() 27 | for i := 0; i < b.N; i++ { 28 | gollection.New([]int{}).ResultAs(&ret) 29 | } 30 | } 31 | 32 | func BenchmarkNewStream(b *testing.B) { 33 | b.ResetTimer() 34 | for i := 0; i < b.N; i++ { 35 | gollection.NewStream([]int{}).Result() 36 | } 37 | } 38 | 39 | func BenchmarkDistinct(b *testing.B) { 40 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 41 | b.ResetTimer() 42 | for i := 0; i < b.N; i++ { 43 | gollection.New(arr).Distinct().Result() 44 | } 45 | } 46 | 47 | func BenchmarkDistinct_Stream(b *testing.B) { 48 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 49 | b.ResetTimer() 50 | for i := 0; i < b.N; i++ { 51 | gollection.NewStream(arr).Distinct().Result() 52 | } 53 | } 54 | 55 | func BenchmarkDistinct_WithoutGollection(b *testing.B) { 56 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 57 | b.ResetTimer() 58 | for i := 0; i < b.N; i++ { 59 | m := make(map[int]bool) 60 | ret := make([]int, 0, len(arr)) 61 | for _, i := range arr { 62 | if _, ok := m[i]; !ok { 63 | ret = append(ret, i) 64 | m[i] = true 65 | } 66 | } 67 | } 68 | } 69 | func BenchmarkDistinctBy(b *testing.B) { 70 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 71 | b.ResetTimer() 72 | for i := 0; i < b.N; i++ { 73 | gollection.New(arr).DistinctBy(func(v int) int { 74 | return v 75 | }).Result() 76 | } 77 | } 78 | 79 | func BenchmarkDistinctBy_Stream(b *testing.B) { 80 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 81 | b.ResetTimer() 82 | for i := 0; i < b.N; i++ { 83 | gollection.NewStream(arr).DistinctBy(func(v int) int { 84 | return v 85 | }).Result() 86 | } 87 | } 88 | 89 | func BenchmarkDistinctBy_WithoutGollection(b *testing.B) { 90 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 91 | b.ResetTimer() 92 | f := func(i int) int { 93 | return i 94 | } 95 | for i := 0; i < b.N; i++ { 96 | m := make(map[int]bool) 97 | ret := make([]int, 0, len(arr)) 98 | for _, i := range arr { 99 | id := f(i) 100 | if _, ok := m[id]; !ok { 101 | ret = append(ret, i) 102 | m[id] = true 103 | } 104 | } 105 | } 106 | } 107 | 108 | func BenchmarkFilter(b *testing.B) { 109 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 110 | b.ResetTimer() 111 | for i := 0; i < b.N; i++ { 112 | gollection.New(arr).Filter(func(v int) bool { 113 | return v > 5 114 | }).Result() 115 | } 116 | } 117 | 118 | func BenchmarkFilter_Stream(b *testing.B) { 119 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 120 | b.ResetTimer() 121 | for i := 0; i < b.N; i++ { 122 | gollection.NewStream(arr).Filter(func(v int) bool { 123 | return v > 5 124 | }).Result() 125 | } 126 | } 127 | 128 | func BenchmarkFilter_WithoutGollection(b *testing.B) { 129 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 130 | b.ResetTimer() 131 | for i := 0; i < b.N; i++ { 132 | ret := make([]int, 0, len(arr)) 133 | for _, i := range arr { 134 | if i > 5 { 135 | ret = append(ret, i) 136 | } 137 | } 138 | } 139 | } 140 | 141 | func BenchmarkFlatMap(b *testing.B) { 142 | arr := [][]int{{0, 0, 1, 1, 2, 2}, {3, 3, 4, 4, 5, 5}, {6, 6, 7, 7, 8, 8, 9, 9}} 143 | b.ResetTimer() 144 | for i := 0; i < b.N; i++ { 145 | gollection.New(arr).FlatMap(func(v int) int { 146 | return v * 2 147 | }).Result() 148 | } 149 | } 150 | 151 | func BenchmarkFlatMap_Stream(b *testing.B) { 152 | arr := [][]int{{0, 0, 1, 1, 2, 2}, {3, 3, 4, 4, 5, 5}, {6, 6, 7, 7, 8, 8, 9, 9}} 153 | b.ResetTimer() 154 | for i := 0; i < b.N; i++ { 155 | gollection.NewStream(arr).FlatMap(func(v int) int { 156 | return v * 2 157 | }).Result() 158 | } 159 | } 160 | 161 | func BenchmarkFlatMap_WithoutGollection(b *testing.B) { 162 | arr := [][]int{{0, 0, 1, 1, 2, 2}, {3, 3, 4, 4, 5, 5}, {6, 6, 7, 7, 8, 8, 9, 9}} 163 | b.ResetTimer() 164 | for i := 0; i < b.N; i++ { 165 | ret := make([]int, 0, len(arr)) 166 | for _, arr2 := range arr { 167 | for _, v := range arr2 { 168 | ret = append(ret, v*2) 169 | } 170 | } 171 | } 172 | } 173 | 174 | func BenchmarkFlatten(b *testing.B) { 175 | arr := [][]int{{0, 0, 1, 1, 2, 2}, {3, 3, 4, 4, 5, 5}, {6, 6, 7, 7, 8, 8, 9, 9}} 176 | b.ResetTimer() 177 | for i := 0; i < b.N; i++ { 178 | gollection.New(arr).Flatten().Result() 179 | } 180 | } 181 | 182 | func BenchmarkFlatten_Stream(b *testing.B) { 183 | arr := [][]int{{0, 0, 1, 1, 2, 2}, {3, 3, 4, 4, 5, 5}, {6, 6, 7, 7, 8, 8, 9, 9}} 184 | b.ResetTimer() 185 | for i := 0; i < b.N; i++ { 186 | gollection.NewStream(arr).Flatten().Result() 187 | } 188 | } 189 | 190 | func BenchmarkFlatten_WithoutGollection(b *testing.B) { 191 | arr := [][]int{{0, 0, 1, 1, 2, 2}, {3, 3, 4, 4, 5, 5}, {6, 6, 7, 7, 8, 8, 9, 9}} 192 | b.ResetTimer() 193 | for i := 0; i < b.N; i++ { 194 | ret := make([]int, 0, len(arr)) 195 | for _, arr2 := range arr { 196 | for _, v := range arr2 { 197 | ret = append(ret, v) 198 | } 199 | } 200 | } 201 | } 202 | 203 | func BenchmarkFold(b *testing.B) { 204 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 205 | b.ResetTimer() 206 | for i := 0; i < b.N; i++ { 207 | gollection.New(arr).Fold(0, func(v1, v2 int) int { 208 | return v1 + v2 209 | }).Result() 210 | } 211 | } 212 | 213 | func BenchmarkFold_Stream(b *testing.B) { 214 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 215 | b.ResetTimer() 216 | for i := 0; i < b.N; i++ { 217 | gollection.NewStream(arr).Fold(0, func(v1, v2 int) int { 218 | return v1 + v2 219 | }).Result() 220 | } 221 | } 222 | 223 | func BenchmarkFold_WithoutGollection(b *testing.B) { 224 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 225 | b.ResetTimer() 226 | for i := 0; i < b.N; i++ { 227 | var ret int 228 | for _, v := range arr { 229 | ret += v 230 | } 231 | } 232 | } 233 | 234 | func BenchmarkMap(b *testing.B) { 235 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 236 | b.ResetTimer() 237 | for i := 0; i < b.N; i++ { 238 | gollection.New(arr).Map(func(v int) int { 239 | return v * 2 240 | }).Result() 241 | } 242 | } 243 | 244 | func BenchmarkMap_Stream(b *testing.B) { 245 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 246 | b.ResetTimer() 247 | for i := 0; i < b.N; i++ { 248 | gollection.NewStream(arr).Map(func(v int) int { 249 | return v * 2 250 | }).Result() 251 | } 252 | } 253 | 254 | func BenchmarkMap_WithoutGollection(b *testing.B) { 255 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 256 | b.ResetTimer() 257 | for i := 0; i < b.N; i++ { 258 | ret := make([]int, 0, len(arr)) 259 | for _, v := range arr { 260 | ret = append(ret, v*2) 261 | } 262 | } 263 | } 264 | 265 | func BenchmarkReduce(b *testing.B) { 266 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 267 | b.ResetTimer() 268 | for i := 0; i < b.N; i++ { 269 | gollection.New(arr).Reduce(func(v1, v2 int) int { 270 | return v1 + v2 271 | }).Result() 272 | } 273 | } 274 | 275 | func BenchmarkReduce_Stream(b *testing.B) { 276 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 277 | b.ResetTimer() 278 | for i := 0; i < b.N; i++ { 279 | gollection.NewStream(arr).Reduce(func(v1, v2 int) int { 280 | return v1 + v2 281 | }).Result() 282 | } 283 | } 284 | 285 | func BenchmarkReduce_WithoutGollection(b *testing.B) { 286 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 287 | b.ResetTimer() 288 | for i := 0; i < b.N; i++ { 289 | var ret int 290 | for _, v := range arr { 291 | ret += v 292 | } 293 | } 294 | } 295 | 296 | func BenchmarkSort(b *testing.B) { 297 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 298 | b.ResetTimer() 299 | for i := 0; i < b.N; i++ { 300 | gollection.New(arr).SortBy(func(v1, v2 int) bool { 301 | return v1 < v2 302 | }).Result() 303 | } 304 | } 305 | 306 | func BenchmarkSort_Stream(b *testing.B) { 307 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 308 | b.ResetTimer() 309 | for i := 0; i < b.N; i++ { 310 | gollection.NewStream(arr).SortBy(func(v1, v2 int) bool { 311 | return v1 < v2 312 | }).Result() 313 | } 314 | } 315 | 316 | func BenchmarkSort_WithoutGollection(b *testing.B) { 317 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 318 | b.ResetTimer() 319 | for i := 0; i < b.N; i++ { 320 | sort.Sort(sort.IntSlice(arr)) 321 | } 322 | } 323 | 324 | func BenchmarkTake(b *testing.B) { 325 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 326 | b.ResetTimer() 327 | for i := 0; i < b.N; i++ { 328 | gollection.New(arr).Take(3).Result() 329 | } 330 | } 331 | 332 | func BenchmarkTake_Stream(b *testing.B) { 333 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 334 | b.ResetTimer() 335 | for i := 0; i < b.N; i++ { 336 | gollection.NewStream(arr).Take(3).Result() 337 | } 338 | } 339 | 340 | func BenchmarkTake_WithoutGollection(b *testing.B) { 341 | arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} 342 | b.ResetTimer() 343 | for i := 0; i < b.N; i++ { 344 | limit := 3 345 | if limit < len(arr) { 346 | limit = len(arr) 347 | } 348 | ret := make([]int, 0, limit) 349 | for i := 0; i < limit; i++ { 350 | ret = append(ret, arr[i]) 351 | } 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | pre: 3 | - curl -L https://raw.github.com/grobins2/gobrew/master/tools/install.sh | sh 4 | - mkdir -p /home/ubuntu/cache 5 | post: 6 | - $HOME/.gobrew/bin/gobrew init - 7 | - $HOME/.gobrew/bin/gobrew install $CIRCLECI_GO_VERSION 8 | - $HOME/.gobrew/bin/gobrew use $CIRCLECI_GO_VERSION 9 | - which go 10 | - go version 11 | 12 | dependencies: 13 | pre: 14 | - which go 15 | - go version 16 | cache_directories: 17 | - /home/ubuntu/cache 18 | - /home/ubuntu/.gobrew 19 | post: 20 | - > 21 | if [[ ! "${CIRCLE_BRANCH}" =~ (release.*|hotfix.*) ]] ; then 22 | go get github.com/mattn/goveralls 23 | go get golang.org/x/tools/cmd/cover 24 | go get github.com/azihsoyn/gocovercache 25 | gocovercache -outdir=/home/ubuntu/cache -coverprofile=profile.cov 26 | goveralls -coverprofile=profile.cov -service=circle-ci -repotoken $COVERALLS_TOKEN 27 | fi 28 | 29 | test: 30 | override: 31 | - go vet ./... 32 | - go test -race ./... 33 | -------------------------------------------------------------------------------- /distinct.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import "reflect" 4 | 5 | func (g *gollection) Distinct() *gollection { 6 | if g.err != nil { 7 | return &gollection{err: g.err} 8 | } 9 | 10 | if g.ch != nil { 11 | return g.distinctStream() 12 | } 13 | 14 | return g.distinct() 15 | 16 | } 17 | 18 | func (g *gollection) DistinctBy(f /*func(v ) */ interface{}) *gollection { 19 | if g.err != nil { 20 | return &gollection{err: g.err} 21 | } 22 | 23 | if g.ch != nil { 24 | return g.distinctByStream(f) 25 | } 26 | 27 | return g.distinctBy(f) 28 | } 29 | 30 | func (g *gollection) distinct() *gollection { 31 | sv, err := g.validateSlice("Distinct") 32 | if err != nil { 33 | return &gollection{err: err} 34 | } 35 | 36 | ret := reflect.MakeSlice(sv.Type(), 0, sv.Len()) 37 | m := make(map[interface{}]bool) 38 | 39 | for i := 0; i < sv.Len(); i++ { 40 | v := sv.Index(i) 41 | if processDistinct(v.Interface(), m) { 42 | ret = reflect.Append(ret, v) 43 | } 44 | } 45 | 46 | return &gollection{ 47 | slice: ret.Interface(), 48 | } 49 | } 50 | 51 | func (g *gollection) distinctStream() *gollection { 52 | next := &gollection{ 53 | ch: make(chan interface{}), 54 | } 55 | 56 | go func() { 57 | var initialized bool 58 | m := make(map[interface{}]bool) 59 | for { 60 | select { 61 | case v, ok := <-g.ch: 62 | if ok { 63 | if !initialized { 64 | // initialize next stream type 65 | next.ch <- v.(reflect.Type) 66 | initialized = true 67 | continue 68 | } 69 | 70 | if processDistinct(v, m) { 71 | next.ch <- v 72 | } 73 | } else { 74 | close(next.ch) 75 | return 76 | } 77 | default: 78 | continue 79 | } 80 | } 81 | }() 82 | return next 83 | } 84 | 85 | func (g *gollection) distinctBy(f interface{}) *gollection { 86 | sv, err := g.validateSlice("DistinctBy") 87 | if err != nil { 88 | return &gollection{err: err} 89 | } 90 | 91 | funcValue, funcType, err := g.validateDistinctByFunc(f) 92 | if err != nil { 93 | return &gollection{err: err} 94 | } 95 | 96 | resultSliceType := reflect.SliceOf(funcType.In(0)) 97 | ret := reflect.MakeSlice(resultSliceType, 0, sv.Len()) 98 | m := make(map[interface{}]bool) 99 | 100 | for i := 0; i < sv.Len(); i++ { 101 | v := sv.Index(i) 102 | if processDistinctBy(funcValue, v, m) { 103 | ret = reflect.Append(ret, v) 104 | } 105 | } 106 | 107 | return &gollection{ 108 | slice: ret.Interface(), 109 | } 110 | } 111 | 112 | func (g *gollection) distinctByStream(f interface{}) *gollection { 113 | next := &gollection{ 114 | ch: make(chan interface{}), 115 | } 116 | 117 | fv, _, err := g.validateDistinctByFunc(f) 118 | if err != nil { 119 | return &gollection{err: err} 120 | } 121 | 122 | go func(fv *reflect.Value) { 123 | var initialized bool 124 | m := make(map[interface{}]bool) 125 | for { 126 | select { 127 | case v, ok := <-g.ch: 128 | if ok { 129 | if !initialized { 130 | // initialize next stream type 131 | next.ch <- v.(reflect.Type) 132 | initialized = true 133 | continue 134 | } 135 | 136 | if processDistinctBy(*fv, reflect.ValueOf(v), m) { 137 | next.ch <- v 138 | } 139 | } else { 140 | close(next.ch) 141 | return 142 | } 143 | default: 144 | continue 145 | } 146 | } 147 | }(&fv) 148 | 149 | return next 150 | } 151 | -------------------------------------------------------------------------------- /distinct_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/azihsoyn/gollection" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestDistinct(t *testing.T) { 11 | assert := assert.New(t) 12 | arr := []int{1, 2, 3, 1, 2, 3} 13 | expect := []int{1, 2, 3} 14 | 15 | res, err := gollection.New(arr).Distinct().Result() 16 | assert.NoError(err) 17 | assert.Equal(expect, res) 18 | } 19 | 20 | func TestDistinct_NotSlice(t *testing.T) { 21 | assert := assert.New(t) 22 | _, err := gollection.New("not slice value").Distinct().Result() 23 | assert.Error(err) 24 | } 25 | 26 | func TestDistinct_HavingError(t *testing.T) { 27 | assert := assert.New(t) 28 | _, err := gollection.New("not slice value").Distinct().Distinct().Result() 29 | assert.Error(err) 30 | 31 | res := []int{} 32 | err = gollection.New("not slice value").Distinct().Distinct().ResultAs(&res) 33 | assert.Error(err) 34 | } 35 | 36 | func TestDistinctBy(t *testing.T) { 37 | assert := assert.New(t) 38 | arr := []string{"aaa", "bb", "c", "ddd", "ee", "f"} 39 | expect := []string{"aaa", "bb", "c"} 40 | 41 | res, err := gollection.New(arr).DistinctBy(func(v string) int { 42 | return len(v) 43 | }).Result() 44 | assert.NoError(err) 45 | assert.Equal(expect, res) 46 | } 47 | func TestDistinctBy_NotSlice(t *testing.T) { 48 | assert := assert.New(t) 49 | _, err := gollection.New("not slice value").DistinctBy(func(v interface{}) interface{} { 50 | return v 51 | }).Result() 52 | assert.Error(err) 53 | } 54 | func TestDistinctBy_NotFunc(t *testing.T) { 55 | assert := assert.New(t) 56 | _, err := gollection.New([]int{}).DistinctBy(0).Result() 57 | assert.Error(err) 58 | } 59 | 60 | func TestDistinctBy_HavingError(t *testing.T) { 61 | assert := assert.New(t) 62 | _, err := gollection.New("not slice value"). 63 | DistinctBy(func(v interface{}) interface{} { 64 | return v 65 | }). 66 | DistinctBy(func(v interface{}) interface{} { 67 | return v 68 | }). 69 | Result() 70 | assert.Error(err) 71 | } 72 | 73 | func TestDistinct_Stream(t *testing.T) { 74 | assert := assert.New(t) 75 | arr := []int{1, 2, 3, 1, 2, 3} 76 | expect := []int{1, 2, 3} 77 | 78 | res, err := gollection.NewStream(arr).Distinct().Result() 79 | assert.NoError(err) 80 | assert.Equal(expect, res) 81 | } 82 | 83 | func TestDistinct_Stream_NotSlice(t *testing.T) { 84 | assert := assert.New(t) 85 | _, err := gollection.NewStream("not slice value").Distinct().Result() 86 | assert.Error(err) 87 | } 88 | 89 | func TestDistinct_Stream_HavingError(t *testing.T) { 90 | assert := assert.New(t) 91 | _, err := gollection.NewStream("not slice value").Distinct().Distinct().Result() 92 | assert.Error(err) 93 | } 94 | 95 | func TestDistinctBy_Stream(t *testing.T) { 96 | assert := assert.New(t) 97 | arr := []string{"aaa", "bb", "c", "ddd", "ee", "f"} 98 | expect := []string{"aaa", "bb", "c"} 99 | 100 | res, err := gollection.NewStream(arr).DistinctBy(func(v string) int { 101 | return len(v) 102 | }).Result() 103 | assert.NoError(err) 104 | assert.Equal(expect, res) 105 | } 106 | func TestDistinctBy_Stream_NotSlice(t *testing.T) { 107 | assert := assert.New(t) 108 | _, err := gollection.NewStream("not slice value").DistinctBy(func(v interface{}) interface{} { 109 | return v 110 | }).Result() 111 | assert.Error(err) 112 | } 113 | func TestDistinctBy_Stream_NotFunc(t *testing.T) { 114 | assert := assert.New(t) 115 | _, err := gollection.NewStream([]int{}).DistinctBy(0).Result() 116 | assert.Error(err) 117 | } 118 | 119 | func TestDistinctBy_Stream_HavingError(t *testing.T) { 120 | assert := assert.New(t) 121 | _, err := gollection.NewStream("not slice value"). 122 | DistinctBy(func(v interface{}) interface{} { 123 | return v 124 | }). 125 | DistinctBy(func(v interface{}) interface{} { 126 | return v 127 | }). 128 | Result() 129 | assert.Error(err) 130 | } 131 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func Example_distinct() { 10 | arr := []int{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10} 11 | 12 | res, err := gollection.New(arr).Distinct().Result() 13 | fmt.Println(res, err) 14 | // Output: [1 2 3 4 5 6 7 8 9 10] 15 | } 16 | 17 | func Example_filter() { 18 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 19 | 20 | res, err := gollection.New(arr).Filter(func(v int) bool { 21 | return v > 5 22 | }).Result() 23 | fmt.Println(res, err) 24 | // Output: [6 7 8 9 10] 25 | } 26 | 27 | func Example_flatMap() { 28 | arr := [][]int{ 29 | {1, 2, 3}, 30 | {4, 5}, 31 | {6, 7, 8, 9, 10}, 32 | } 33 | 34 | res, err := gollection.New(arr).FlatMap(func(v int) int { 35 | return v * 2 36 | }).Result() 37 | fmt.Println(res, err) 38 | // Output: [2 4 6 8 10 12 14 16 18 20] 39 | } 40 | 41 | func Example_flatten() { 42 | arr := [][]int{ 43 | {1, 2, 3}, 44 | {4, 5}, 45 | {6, 7, 8, 9, 10}, 46 | } 47 | 48 | res, err := gollection.New(arr).Flatten().Result() 49 | fmt.Println(res, err) 50 | // Output: [1 2 3 4 5 6 7 8 9 10] 51 | } 52 | 53 | func Example_fold() { 54 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 55 | 56 | res, err := gollection.New(arr).Fold(100, func(v1, v2 int) int { 57 | return v1 + v2 58 | }).Result() 59 | fmt.Println(res, err) 60 | // Output: 155 61 | } 62 | 63 | func Example_map() { 64 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 65 | 66 | res, err := gollection.New(arr).Map(func(v int) int { 67 | return v * 2 68 | }).Result() 69 | fmt.Println(res, err) 70 | // Output: [2 4 6 8 10 12 14 16 18 20] 71 | } 72 | 73 | func Example_reduce() { 74 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 75 | 76 | res, err := gollection.New(arr).Reduce(func(v1, v2 int) int { 77 | return v1 + v2 78 | }).Result() 79 | fmt.Println(res, err) 80 | // Output: 55 81 | } 82 | 83 | func Example_skip() { 84 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 85 | 86 | res, err := gollection.New(arr).Skip(3).Result() 87 | fmt.Println(res, err) 88 | res, err = gollection.New(arr).Skip(30).Result() 89 | fmt.Println(res, err) 90 | // Output: [4 5 6 7 8 9 10] 91 | // [] 92 | } 93 | 94 | func Example_sort() { 95 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} 96 | 97 | res, err := gollection.New(arr).SortBy(func(v1, v2 int) bool { 98 | return v1 < v2 99 | }).Result() 100 | fmt.Println(arr) 101 | fmt.Println(res, err) 102 | // Output: [1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1] 103 | // [1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10] 104 | } 105 | 106 | func Example_take() { 107 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 108 | 109 | res, err := gollection.New(arr).Take(3).Result() 110 | fmt.Println(res, err) 111 | res, err = gollection.New(arr).Take(30).Result() 112 | fmt.Println(res, err) 113 | // Output: [1 2 3] 114 | // [1 2 3 4 5 6 7 8 9 10] 115 | } 116 | 117 | func Example_customType() { 118 | type User struct { 119 | ID int 120 | Name string 121 | } 122 | 123 | in := []User{ 124 | {ID: 1, Name: "aaa"}, 125 | {ID: 2, Name: "bbb"}, 126 | {ID: 3, Name: "ccc"}, 127 | {ID: 4, Name: "ddd"}, 128 | {ID: 5, Name: "eee"}, 129 | {ID: 6, Name: "fff"}, 130 | {ID: 7, Name: "ggg"}, 131 | } 132 | res, err := gollection.New(in).Filter(func(v User) bool { 133 | return v.ID > 5 134 | }).Result() 135 | fmt.Printf("%#v %#v\n", res.([]User), err) 136 | // Output: []gollection_test.User{gollection_test.User{ID:6, Name:"fff"}, gollection_test.User{ID:7, Name:"ggg"}} 137 | } 138 | 139 | func Example_customTypeWithResultAs() { 140 | type User struct { 141 | ID int 142 | Name string 143 | } 144 | 145 | in := []User{ 146 | {ID: 1, Name: "aaa"}, 147 | {ID: 2, Name: "bbb"}, 148 | {ID: 3, Name: "ccc"}, 149 | {ID: 4, Name: "ddd"}, 150 | {ID: 5, Name: "eee"}, 151 | {ID: 6, Name: "fff"}, 152 | {ID: 7, Name: "ggg"}, 153 | } 154 | var res []User 155 | err := gollection.New(in).Filter(func(v User) bool { 156 | return v.ID > 5 157 | }).ResultAs(&res) 158 | fmt.Printf("%#v %#v\n", res, err) 159 | // Output: []gollection_test.User{gollection_test.User{ID:6, Name:"fff"}, gollection_test.User{ID:7, Name:"ggg"}} 160 | } 161 | -------------------------------------------------------------------------------- /examples/custom_type/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | type User struct { 10 | ID int 11 | Name string 12 | } 13 | 14 | func main() { 15 | in := []User{ 16 | {ID: 1, Name: "aaa"}, 17 | {ID: 2, Name: "bbb"}, 18 | {ID: 3, Name: "ccc"}, 19 | {ID: 4, Name: "ddd"}, 20 | {ID: 5, Name: "eee"}, 21 | {ID: 6, Name: "fff"}, 22 | {ID: 7, Name: "ggg"}, 23 | } 24 | var out []User 25 | err := gollection.New(in).Filter(func(v User) bool { 26 | return v.ID > 5 27 | }).ResultAs(&out) 28 | fmt.Printf("out with ResultAs : %#v\n", out) 29 | fmt.Println("err : ", err) 30 | 31 | ret, _ := gollection.New(in).Filter(func(v User) bool { 32 | return v.ID > 5 33 | }).Result() 34 | out = ret.([]User) 35 | fmt.Printf("out with type assertion : %#v\n", out) 36 | } 37 | -------------------------------------------------------------------------------- /examples/distinct/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10} 11 | 12 | res, _ := gollection.New(arr).Distinct().Result() 13 | fmt.Println("origin : ", arr) 14 | fmt.Println("ret : ", res) // {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 15 | 16 | type user struct { 17 | ID int64 18 | Name string 19 | } 20 | users := []user{ 21 | {ID: 1, Name: "Ann"}, 22 | {ID: 2, Name: "Bob"}, 23 | {ID: 3, Name: "Charles"}, 24 | {ID: 4, Name: "Ann"}, 25 | {ID: 5, Name: "Bob"}, 26 | {ID: 6, Name: "Charles"}, 27 | } 28 | 29 | res, _ = gollection.New(users).DistinctBy(func(v user) string { 30 | return v.Name 31 | }).Result() 32 | fmt.Println("origin : ", users) 33 | fmt.Println("ret : ", res) 34 | } 35 | -------------------------------------------------------------------------------- /examples/filter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 11 | 12 | res, _ := gollection.New(arr).Filter(func(v int) bool { 13 | return v > 5 14 | }).Result() 15 | fmt.Println("origin : ", arr) 16 | fmt.Println("ret : ", res) 17 | } 18 | -------------------------------------------------------------------------------- /examples/flatMap/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := [][]int{ 11 | {1, 2, 3}, 12 | {4, 5}, 13 | {6, 7, 8, 9, 10}, 14 | } 15 | 16 | res, _ := gollection.New(arr).FlatMap(func(v int) int { 17 | return v * 2 18 | }).Result() 19 | fmt.Println("origin : ", arr) 20 | fmt.Println("ret : ", res) 21 | } 22 | -------------------------------------------------------------------------------- /examples/flatten/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := [][]int{ 11 | {1, 2, 3}, 12 | {4, 5}, 13 | {6, 7, 8, 9, 10}, 14 | } 15 | 16 | res, _ := gollection.New(arr).Flatten().Result() 17 | fmt.Println("origin : ", arr) 18 | fmt.Println("ret : ", res) 19 | } 20 | -------------------------------------------------------------------------------- /examples/fold/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 11 | 12 | res, _ := gollection.New(arr).Fold(100, func(v1, v2 int) int { 13 | return v1 + v2 14 | }).Result() 15 | fmt.Println("origin : ", arr) 16 | fmt.Println("ret : ", res) 17 | } 18 | -------------------------------------------------------------------------------- /examples/map/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 11 | 12 | res, _ := gollection.New(arr).Map(func(v int) int { 13 | return v * 2 14 | }).Result() 15 | fmt.Println("origin : ", arr) 16 | fmt.Println("ret : ", res) 17 | } 18 | -------------------------------------------------------------------------------- /examples/reduce/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 11 | 12 | res, _ := gollection.New(arr).Reduce(func(v1, v2 int) int { 13 | return v1 + v2 14 | }).Result() 15 | fmt.Println("origin : ", arr) 16 | fmt.Println("ret : ", res) 17 | } 18 | -------------------------------------------------------------------------------- /examples/skip/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 11 | 12 | res, _ := gollection.New(arr).Skip(3).Result() 13 | fmt.Println("origin : ", arr) 14 | fmt.Println("ret : ", res) // {4, 5, 6, 7, 8, 9, 10} 15 | 16 | res, _ = gollection.New(arr).Skip(30).Result() 17 | fmt.Println("origin : ", arr) 18 | fmt.Println("ret : ", res) // {} 19 | } 20 | -------------------------------------------------------------------------------- /examples/sort/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} 11 | 12 | res, _ := gollection.New(arr).SortBy(func(v1, v2 int) bool { 13 | return v1 < v2 14 | }).Result() 15 | fmt.Println("origin : ", arr) 16 | fmt.Println("ret : ", res) 17 | } 18 | -------------------------------------------------------------------------------- /examples/stream/distinct/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10} 11 | 12 | res, err := gollection.NewStream(arr).Distinct().Result() 13 | fmt.Println("origin : ", arr) 14 | fmt.Println("ret : ", res) // {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 15 | fmt.Println("error : ", err) 16 | 17 | type user struct { 18 | ID int64 19 | Name string 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/stream/filter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/azihsoyn/gollection" 8 | ) 9 | 10 | func main() { 11 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 12 | 13 | fmt.Println("start :", time.Now()) 14 | res, _ := gollection.NewStream(arr).Filter(func(v int) bool { 15 | time.Sleep(100 * time.Millisecond) 16 | fmt.Println("process 1st filter") 17 | return true 18 | }).Filter(func(v int) bool { 19 | time.Sleep(100 * time.Millisecond) 20 | fmt.Println("process 2st filter") 21 | return true 22 | }).Result() 23 | fmt.Println("origin : ", arr) 24 | fmt.Println("ret : ", res) 25 | fmt.Println("end :", time.Now()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/stream/flatMap/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := [][]int{ 11 | {1, 2, 3}, 12 | {4, 5}, 13 | {6, 7, 8, 9, 10}, 14 | } 15 | 16 | res, _ := gollection.NewStream(arr).FlatMap(func(v int) int { 17 | return v * 2 18 | }).Result() 19 | fmt.Println("origin : ", arr) 20 | fmt.Println("ret : ", res) 21 | } 22 | -------------------------------------------------------------------------------- /examples/stream/flatten/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := [][]int{ 11 | {1, 2, 3}, 12 | {4, 5}, 13 | {6, 7, 8, 9, 10}, 14 | } 15 | 16 | res, _ := gollection.NewStream(arr).Flatten().Result() 17 | fmt.Println("origin : ", arr) 18 | fmt.Println("ret : ", res) 19 | } 20 | -------------------------------------------------------------------------------- /examples/stream/fold/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 11 | 12 | res, _ := gollection.NewStream(arr).Fold(100, func(v1, v2 int) int { 13 | return v1 + v2 14 | }).Result() 15 | fmt.Println("origin : ", arr) 16 | fmt.Println("ret : ", res) 17 | } 18 | -------------------------------------------------------------------------------- /examples/stream/map/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/azihsoyn/gollection" 8 | ) 9 | 10 | func main() { 11 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 12 | 13 | fmt.Println("start :", time.Now()) 14 | res, _ := gollection.NewStream(arr).Map(func(v int) int { 15 | time.Sleep(100 * time.Millisecond) 16 | fmt.Println("process 1st map") 17 | return v * 2 18 | }).Map(func(v int) int { 19 | time.Sleep(100 * time.Millisecond) 20 | fmt.Println("process 2nd map") 21 | return v * 2 22 | }).Result() 23 | fmt.Println("origin : ", arr) 24 | fmt.Println("ret : ", res) 25 | fmt.Println("end :", time.Now()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/stream/reduce/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 11 | 12 | res, _ := gollection.NewStream(arr).Reduce(func(v1, v2 int) int { 13 | return v1 + v2 14 | }).Result() 15 | fmt.Println("origin : ", arr) 16 | fmt.Println("ret : ", res) 17 | } 18 | -------------------------------------------------------------------------------- /examples/stream/sort/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} 11 | 12 | res, _ := gollection.NewStream(arr).SortBy(func(v1, v2 int) bool { 13 | return v1 < v2 14 | }).Result() 15 | fmt.Println("origin : ", arr) 16 | fmt.Println("ret : ", res) 17 | } 18 | -------------------------------------------------------------------------------- /examples/stream/take/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 11 | 12 | res, _ := gollection.NewStream(arr).Take(3).Result() 13 | fmt.Println("origin : ", arr) 14 | fmt.Println("ret : ", res) // {1, 2, 3} 15 | 16 | res, _ = gollection.NewStream(arr).Take(30).Result() 17 | fmt.Println("origin : ", arr) 18 | fmt.Println("ret : ", res) // {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 19 | } 20 | -------------------------------------------------------------------------------- /examples/take/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/azihsoyn/gollection" 7 | ) 8 | 9 | func main() { 10 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 11 | 12 | res, _ := gollection.New(arr).Take(3).Result() 13 | fmt.Println("origin : ", arr) 14 | fmt.Println("ret : ", res) // {1, 2, 3} 15 | 16 | res, _ = gollection.New(arr).Take(30).Result() 17 | fmt.Println("origin : ", arr) 18 | fmt.Println("ret : ", res) // {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 19 | } 20 | -------------------------------------------------------------------------------- /filter.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import "reflect" 4 | 5 | func (g *gollection) Filter(f /* func(v ) bool */ interface{}) *gollection { 6 | if g.err != nil { 7 | return &gollection{err: g.err} 8 | } 9 | 10 | if g.ch != nil { 11 | return g.filterStream(f) 12 | } 13 | return g.filter(f) 14 | } 15 | 16 | func (g *gollection) filter(f interface{}) *gollection { 17 | sv, err := g.validateSlice("Filter") 18 | if err != nil { 19 | return &gollection{err: err} 20 | } 21 | 22 | funcValue, funcType, err := g.validateFilterFunc(f) 23 | if err != nil { 24 | return &gollection{err: err} 25 | } 26 | 27 | resultSliceType := reflect.SliceOf(funcType.In(0)) 28 | ret := reflect.MakeSlice(resultSliceType, 0, sv.Len()) 29 | 30 | for i := 0; i < sv.Len(); i++ { 31 | v := sv.Index(i) 32 | if processFilter(funcValue, v) { 33 | ret = reflect.Append(ret, v) 34 | } 35 | } 36 | 37 | return &gollection{ 38 | slice: ret.Interface(), 39 | } 40 | } 41 | 42 | func (g *gollection) filterStream(f interface{}) *gollection { 43 | next := &gollection{ 44 | ch: make(chan interface{}), 45 | } 46 | 47 | funcValue, funcType, err := g.validateFilterFunc(f) 48 | if err != nil { 49 | return &gollection{ 50 | err: err, 51 | } 52 | } 53 | 54 | var initialized bool 55 | go func() { 56 | for { 57 | select { 58 | case v, ok := <-g.ch: 59 | if ok { 60 | if !initialized { 61 | // initialize next stream type 62 | next.ch <- reflect.SliceOf(funcType.In(0)) 63 | initialized = true 64 | continue 65 | } 66 | 67 | if processFilter(funcValue, reflect.ValueOf(v)) { 68 | next.ch <- v 69 | } 70 | } else { 71 | close(next.ch) 72 | return 73 | } 74 | default: 75 | continue 76 | } 77 | } 78 | }() 79 | return next 80 | } 81 | -------------------------------------------------------------------------------- /filter_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/azihsoyn/gollection" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestFilter(t *testing.T) { 11 | assert := assert.New(t) 12 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 13 | expect := []int{6, 7, 8, 9, 10} 14 | 15 | res, err := gollection.New(arr).Filter(func(v int) bool { 16 | return v > 5 17 | }).Result() 18 | assert.NoError(err) 19 | assert.Equal(expect, res) 20 | } 21 | func TestFilter_NotSlice(t *testing.T) { 22 | assert := assert.New(t) 23 | _, err := gollection.New("not slice value").Filter(func(v interface{}) bool { 24 | return true 25 | }).Result() 26 | assert.Error(err) 27 | } 28 | func TestFilter_NotFunc(t *testing.T) { 29 | assert := assert.New(t) 30 | _, err := gollection.New([]int{0}).Filter(0).Result() 31 | assert.Error(err) 32 | } 33 | 34 | func TestFilter_HavingError(t *testing.T) { 35 | assert := assert.New(t) 36 | _, err := gollection.New("not slice value"). 37 | Filter(func(v interface{}) bool { 38 | return true 39 | }). 40 | Filter(func(v interface{}) bool { 41 | return true 42 | }). 43 | Result() 44 | assert.Error(err) 45 | } 46 | 47 | func TestFilter_Stream(t *testing.T) { 48 | assert := assert.New(t) 49 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 50 | expect := []int{6, 7, 8, 9, 10} 51 | 52 | res, err := gollection.NewStream(arr).Filter(func(v int) bool { 53 | return v > 5 54 | }).Result() 55 | assert.NoError(err) 56 | assert.Equal(expect, res) 57 | } 58 | 59 | func TestFilter_Stream_NotSlice(t *testing.T) { 60 | assert := assert.New(t) 61 | _, err := gollection.NewStream("not slice value").Filter(func(v interface{}) bool { 62 | return true 63 | }).Result() 64 | assert.Error(err) 65 | } 66 | 67 | func TestFilter_Stream_NotFunc(t *testing.T) { 68 | assert := assert.New(t) 69 | _, err := gollection.NewStream([]int{0}).Filter(0).Result() 70 | assert.Error(err) 71 | } 72 | 73 | func TestFilter_Stream_HavingError(t *testing.T) { 74 | assert := assert.New(t) 75 | _, err := gollection.NewStream("not slice value"). 76 | Filter(func(v interface{}) bool { 77 | return true 78 | }). 79 | Filter(func(v interface{}) bool { 80 | return true 81 | }). 82 | Result() 83 | assert.Error(err) 84 | } 85 | -------------------------------------------------------------------------------- /flat_map.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func (g *gollection) FlatMap(f /*func(v ) */ interface{}) *gollection { 9 | if g.err != nil { 10 | return &gollection{err: g.err} 11 | } 12 | 13 | if g.ch != nil { 14 | return g.flatMapStream(f) 15 | } 16 | 17 | return g.flatMap(f) 18 | } 19 | 20 | func (g *gollection) flatMap(f interface{}) *gollection { 21 | sv, err := g.validateSlice("FlatMap") 22 | if err != nil { 23 | return &gollection{err: err} 24 | } 25 | 26 | if _, err := g.validateSliceOfSlice("FlatMap"); err != nil { 27 | return &gollection{err: err} 28 | } 29 | 30 | funcValue, funcType, err := g.validateFlatMapFunc(f) 31 | if err != nil { 32 | return &gollection{err: err} 33 | } 34 | 35 | resultSliceType := reflect.SliceOf(funcType.Out(0)) 36 | ret := reflect.MakeSlice(resultSliceType, 0, sv.Len()) 37 | 38 | // avoid "panic: reflect: call of reflect.Value.Interface on zero Value" 39 | // see https://github.com/azihsoyn/gollection/issues/7 40 | if sv.Len() == 0 { 41 | return &gollection{ 42 | slice: ret.Interface(), 43 | } 44 | } 45 | 46 | for i := 0; i < sv.Len(); i++ { 47 | v := sv.Index(i).Interface() 48 | svv := reflect.ValueOf(v) 49 | for j := 0; j < svv.Len(); j++ { 50 | v := processMapFunc(funcValue, svv.Index(j)) 51 | ret = reflect.Append(ret, v) 52 | } 53 | } 54 | 55 | return &gollection{ 56 | slice: ret.Interface(), 57 | err: nil, 58 | } 59 | } 60 | 61 | func (g *gollection) flatMapStream(f interface{}) *gollection { 62 | next := &gollection{ 63 | ch: make(chan interface{}), 64 | } 65 | 66 | funcValue, funcType, err := g.validateFlatMapFunc(f) 67 | if err != nil { 68 | return &gollection{err: err} 69 | } 70 | 71 | var initialized bool 72 | go func() { 73 | for { 74 | select { 75 | case v, ok := <-g.ch: 76 | if ok { 77 | if !initialized { 78 | // initialze next stream type 79 | currentType := v.(reflect.Type).Elem() 80 | if currentType.Kind() != reflect.Slice { 81 | next.ch <- fmt.Errorf("gollection.FlatMap called with non-slice-of-slice value of type %s", currentType) 82 | } 83 | next.ch <- reflect.SliceOf(funcType.Out(0)) 84 | initialized = true 85 | continue 86 | } 87 | 88 | svv := reflect.ValueOf(v) 89 | for j := 0; j < svv.Len(); j++ { 90 | v := processMapFunc(funcValue, svv.Index(j)) 91 | next.ch <- v.Interface() 92 | } 93 | } else { 94 | close(next.ch) 95 | return 96 | } 97 | default: 98 | continue 99 | } 100 | } 101 | }() 102 | 103 | return next 104 | } 105 | -------------------------------------------------------------------------------- /flat_map_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/azihsoyn/gollection" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestFlatMap(t *testing.T) { 11 | assert := assert.New(t) 12 | 13 | arr := [][]int{ 14 | {1, 2, 3}, 15 | {4, 5}, 16 | {6, 7, 8, 9, 10}, 17 | } 18 | expect := []int{2, 4, 6, 8, 10, 12, 14, 16, 18, 20} 19 | 20 | res, err := gollection.New(arr).FlatMap(func(v int) int { 21 | return v * 2 22 | }).Result() 23 | assert.NoError(err) 24 | assert.Equal(expect, res) 25 | } 26 | 27 | func TestFlatMap_InterfaceSlice(t *testing.T) { 28 | assert := assert.New(t) 29 | arr := [][]interface{}{ 30 | []interface{}{1, 2, 3}, 31 | []interface{}{"a", "b"}, 32 | nil, 33 | } 34 | expect := []interface{}{2, 4, 6, "a", "b"} 35 | 36 | res, err := gollection.New(arr).FlatMap(func(v interface{}) interface{} { 37 | if n, ok := v.(int); ok { 38 | return n * 2 39 | } 40 | return v 41 | }).Result() 42 | assert.NoError(err) 43 | assert.Equal(expect, res) 44 | } 45 | 46 | func TestFlatMap_EmptySlice(t *testing.T) { 47 | assert := assert.New(t) 48 | expect := []string{} 49 | res, err := gollection.New([][]int{}).FlatMap(func(v int) string { 50 | return "" 51 | }).Result() 52 | assert.NoError(err) 53 | assert.Equal(expect, res) 54 | } 55 | 56 | func TestFlatMap_NotFunc(t *testing.T) { 57 | assert := assert.New(t) 58 | _, err := gollection.New([][]int{}).FlatMap(0).Result() 59 | assert.Error(err) 60 | } 61 | 62 | func TestFlatMap_NonSliceOfSlice(t *testing.T) { 63 | assert := assert.New(t) 64 | _, err := gollection.New([]int{}).FlatMap(func(v int) string { 65 | return "" 66 | }).Result() 67 | assert.Error(err) 68 | } 69 | 70 | func TestFlatMap_NotSlice(t *testing.T) { 71 | assert := assert.New(t) 72 | _, err := gollection.New("not slice value").FlatMap(func(v interface{}) interface{} { 73 | return "" 74 | }).Result() 75 | assert.Error(err) 76 | } 77 | 78 | func TestFlatMap_HavingError(t *testing.T) { 79 | assert := assert.New(t) 80 | _, err := gollection.New("not slice value"). 81 | FlatMap(func(v interface{}) interface{} { 82 | return "" 83 | }). 84 | FlatMap(func(v interface{}) interface{} { 85 | return "" 86 | }). 87 | Result() 88 | assert.Error(err) 89 | } 90 | 91 | func TestFlatMap_Stream(t *testing.T) { 92 | assert := assert.New(t) 93 | 94 | arr := [][]int{ 95 | {1, 2, 3}, 96 | {4, 5}, 97 | {6, 7, 8, 9, 10}, 98 | } 99 | expect := []int{2, 4, 6, 8, 10, 12, 14, 16, 18, 20} 100 | 101 | res, err := gollection.NewStream(arr).FlatMap(func(v int) int { 102 | return v * 2 103 | }).Result() 104 | assert.NoError(err) 105 | assert.Equal(expect, res) 106 | } 107 | 108 | func TestFlatMap_Stream_InterfaceSlice(t *testing.T) { 109 | assert := assert.New(t) 110 | arr := [][]interface{}{ 111 | []interface{}{1, 2, 3}, 112 | []interface{}{"a", "b"}, 113 | nil, 114 | } 115 | expect := []interface{}{2, 4, 6, "a", "b"} 116 | 117 | res, err := gollection.NewStream(arr).FlatMap(func(v interface{}) interface{} { 118 | if n, ok := v.(int); ok { 119 | return n * 2 120 | } 121 | return v 122 | }).Result() 123 | assert.NoError(err) 124 | assert.Equal(expect, res) 125 | } 126 | 127 | func TestFlatMap_Stream_EmptySlice(t *testing.T) { 128 | assert := assert.New(t) 129 | expect := []string{} 130 | res, err := gollection.NewStream([][]int{}).FlatMap(func(v int) string { 131 | return "" 132 | }).Result() 133 | assert.NoError(err) 134 | assert.Equal(expect, res) 135 | } 136 | 137 | func TestFlatMap_Stream_NotFunc(t *testing.T) { 138 | assert := assert.New(t) 139 | _, err := gollection.NewStream([][]int{}).FlatMap(0).Result() 140 | assert.Error(err) 141 | } 142 | 143 | func TestFlatMap_Stream_NonSliceOfSlice(t *testing.T) { 144 | assert := assert.New(t) 145 | _, err := gollection.NewStream([]int{}).FlatMap(func(v int) string { 146 | return "" 147 | }).Result() 148 | assert.Error(err) 149 | } 150 | 151 | func TestFlatMap_Stream_NotSlice(t *testing.T) { 152 | assert := assert.New(t) 153 | _, err := gollection.NewStream("not slice value").FlatMap(func(v interface{}) interface{} { 154 | return "" 155 | }).Result() 156 | assert.Error(err) 157 | } 158 | 159 | func TestFlatMap_Stream_HavingError(t *testing.T) { 160 | assert := assert.New(t) 161 | _, err := gollection.NewStream("not slice value"). 162 | FlatMap(func(v interface{}) interface{} { 163 | return "" 164 | }). 165 | FlatMap(func(v interface{}) interface{} { 166 | return "" 167 | }). 168 | Result() 169 | assert.Error(err) 170 | } 171 | -------------------------------------------------------------------------------- /flatten.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func (g *gollection) Flatten() *gollection { 9 | if g.err != nil { 10 | return &gollection{err: g.err} 11 | } 12 | 13 | if g.ch != nil { 14 | return g.flattenStream() 15 | } 16 | return g.flatten() 17 | } 18 | 19 | func (g *gollection) flatten() *gollection { 20 | sv, err := g.validateSlice("Flatten") 21 | if err != nil { 22 | return &gollection{err: err} 23 | } 24 | 25 | currentType, err := g.validateSliceOfSlice("Flatten") 26 | if err != nil { 27 | return &gollection{err: err} 28 | } 29 | 30 | // init 31 | ret := reflect.MakeSlice(currentType, 0, sv.Len()) 32 | 33 | for i := 0; i < sv.Len(); i++ { 34 | v := sv.Index(i).Interface() 35 | svv := reflect.ValueOf(v) 36 | for j := 0; j < svv.Len(); j++ { 37 | ret = reflect.Append(ret, svv.Index(j)) 38 | } 39 | } 40 | 41 | return &gollection{ 42 | slice: ret.Interface(), 43 | err: nil, 44 | } 45 | } 46 | 47 | func (g *gollection) flattenStream() *gollection { 48 | next := &gollection{ 49 | ch: make(chan interface{}), 50 | } 51 | 52 | var initialized bool 53 | go func() { 54 | for { 55 | select { 56 | case v, ok := <-g.ch: 57 | if ok { 58 | // initialze next stream type 59 | if !initialized { 60 | currentType := v.(reflect.Type).Elem() 61 | if currentType.Kind() != reflect.Slice { 62 | next.ch <- fmt.Errorf("gollection.Flatten called with non-slice-of-slice value of type %s", currentType) 63 | } 64 | next.ch <- currentType 65 | initialized = true 66 | continue 67 | } 68 | 69 | svv := reflect.ValueOf(v) 70 | for j := 0; j < svv.Len(); j++ { 71 | next.ch <- svv.Index(j).Interface() 72 | } 73 | } else { 74 | close(next.ch) 75 | return 76 | } 77 | default: 78 | continue 79 | } 80 | } 81 | }() 82 | return next 83 | } 84 | -------------------------------------------------------------------------------- /flatten_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/azihsoyn/gollection" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestFlatten(t *testing.T) { 11 | assert := assert.New(t) 12 | arr := [][]int{ 13 | {1, 2, 3}, 14 | {4, 5}, 15 | {6, 7, 8, 9, 10}, 16 | } 17 | expect := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 18 | 19 | res, err := gollection.New(arr).Flatten().Result() 20 | assert.NoError(err) 21 | assert.Equal(expect, res) 22 | 23 | arr = [][]int{ 24 | {1}, 25 | nil, 26 | {2}, 27 | } 28 | expect = []int{1, 2} 29 | 30 | res, err = gollection.New(arr).Flatten().Result() 31 | assert.NoError(err) 32 | assert.Equal(expect, res) 33 | } 34 | 35 | func TestFlatten_EmptySlice(t *testing.T) { 36 | assert := assert.New(t) 37 | arr := [][]int{} 38 | expect := []int{} 39 | 40 | res, err := gollection.New(arr).Flatten().Result() 41 | assert.NoError(err) 42 | assert.Equal(expect, res) 43 | } 44 | 45 | func TestFlatten_InterfaceSlice(t *testing.T) { 46 | assert := assert.New(t) 47 | arr := [][]interface{}{ 48 | []interface{}{1, 2, 3}, 49 | []interface{}{"a", "b"}, 50 | } 51 | expect := []interface{}{1, 2, 3, "a", "b"} 52 | 53 | res, err := gollection.New(arr).Flatten().Result() 54 | assert.NoError(err) 55 | assert.Equal(expect, res) 56 | } 57 | 58 | func TestFlatten_NotSliceOfSlice(t *testing.T) { 59 | assert := assert.New(t) 60 | 61 | _, err := gollection.New([]int{}).Flatten().Result() 62 | assert.Error(err) 63 | } 64 | 65 | func TestFlatten_NotSlice(t *testing.T) { 66 | assert := assert.New(t) 67 | 68 | _, err := gollection.New("not slice value").Flatten().Result() 69 | assert.Error(err) 70 | } 71 | 72 | func TestFlatten_HavingError(t *testing.T) { 73 | assert := assert.New(t) 74 | 75 | _, err := gollection.New("not slice value").Flatten().Flatten().Result() 76 | assert.Error(err) 77 | } 78 | 79 | func TestFlatten_Stream(t *testing.T) { 80 | assert := assert.New(t) 81 | arr := [][]int{ 82 | {1, 2, 3}, 83 | {4, 5}, 84 | {6, 7, 8, 9, 10}, 85 | } 86 | expect := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 87 | 88 | res, err := gollection.NewStream(arr).Flatten().Result() 89 | assert.NoError(err) 90 | assert.Equal(expect, res) 91 | 92 | arr = [][]int{ 93 | {1}, 94 | nil, 95 | {2}, 96 | } 97 | expect = []int{1, 2} 98 | 99 | res, err = gollection.NewStream(arr).Flatten().Result() 100 | assert.NoError(err) 101 | assert.Equal(expect, res) 102 | } 103 | 104 | func TestFlatten_Stream_EmptySlice(t *testing.T) { 105 | assert := assert.New(t) 106 | arr := [][]int{} 107 | expect := []int{} 108 | 109 | res, err := gollection.NewStream(arr).Flatten().Result() 110 | assert.NoError(err) 111 | assert.Equal(expect, res) 112 | } 113 | 114 | func TestFlatten_Stream_InterfaceSlice(t *testing.T) { 115 | assert := assert.New(t) 116 | arr := [][]interface{}{ 117 | []interface{}{1, 2, 3}, 118 | []interface{}{"a", "b"}, 119 | } 120 | expect := []interface{}{1, 2, 3, "a", "b"} 121 | 122 | res, err := gollection.NewStream(arr).Flatten().Result() 123 | assert.NoError(err) 124 | assert.Equal(expect, res) 125 | } 126 | 127 | func TestFlatten_Stream_NotSliceOfSlice(t *testing.T) { 128 | assert := assert.New(t) 129 | 130 | _, err := gollection.NewStream([]int{}).Flatten().Result() 131 | assert.Error(err) 132 | } 133 | 134 | func TestFlatten_Stream_NotSlice(t *testing.T) { 135 | assert := assert.New(t) 136 | 137 | _, err := gollection.NewStream("not slice value").Flatten().Result() 138 | assert.Error(err) 139 | } 140 | 141 | func TestFlatten_Stream_HavingError(t *testing.T) { 142 | assert := assert.New(t) 143 | 144 | _, err := gollection.NewStream("not slice value").Flatten().Flatten().Result() 145 | assert.Error(err) 146 | } 147 | -------------------------------------------------------------------------------- /fold.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import ( 4 | "reflect" 5 | "sync" 6 | ) 7 | 8 | func (g *gollection) Fold(v0 interface{}, f /* func(v1, v2 ) */ interface{}) *gollection { 9 | if g.err != nil { 10 | return &gollection{err: g.err} 11 | } 12 | 13 | if g.ch != nil { 14 | return g.foldStream(v0, f) 15 | } 16 | 17 | return g.fold(v0, f) 18 | } 19 | 20 | func (g *gollection) fold(v0 interface{}, f interface{}) *gollection { 21 | sv, err := g.validateSlice("Fold") 22 | if err != nil { 23 | return &gollection{err: err} 24 | } 25 | 26 | if sv.Len() < 1 { 27 | return &gollection{val: v0} 28 | } 29 | 30 | funcValue, _, err := g.validateFoldFunc(f) 31 | if err != nil { 32 | return &gollection{err: err} 33 | } 34 | 35 | ret := v0 36 | for i := 0; i < sv.Len(); i++ { 37 | v1 := reflect.ValueOf(ret) 38 | v2 := sv.Index(i) 39 | ret = processReduceFunc(funcValue, v1, v2).Interface() 40 | } 41 | 42 | return &gollection{ 43 | val: ret, 44 | } 45 | 46 | } 47 | 48 | func (g *gollection) foldStream(v0 interface{}, f interface{}) *gollection { 49 | funcValue, _, err := g.validateFoldFunc(f) 50 | if err != nil { 51 | return &gollection{err: err} 52 | } 53 | 54 | var ret interface{} 55 | var initialized bool 56 | wg := sync.WaitGroup{} 57 | wg.Add(1) 58 | go func(wg *sync.WaitGroup, ret *interface{}) { 59 | *ret = v0 60 | 61 | for { 62 | select { 63 | case v, ok := <-g.ch: 64 | if ok { 65 | // skip first item(reflect.Type) 66 | if !initialized { 67 | initialized = true 68 | continue 69 | } 70 | 71 | v1 := reflect.ValueOf(*ret) 72 | v2 := reflect.ValueOf(v) 73 | *ret = processReduceFunc(funcValue, v1, v2).Interface() 74 | } else { 75 | (*wg).Done() 76 | return 77 | } 78 | default: 79 | continue 80 | } 81 | } 82 | }(&wg, &ret) 83 | wg.Wait() 84 | 85 | return &gollection{ 86 | val: ret, 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /fold_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/azihsoyn/gollection" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestFold(t *testing.T) { 11 | assert := assert.New(t) 12 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 13 | expect := 155 14 | 15 | res, err := gollection.New(arr).Fold(100, func(v1, v2 int) int { 16 | return v1 + v2 17 | }).Result() 18 | assert.NoError(err) 19 | assert.Equal(expect, res) 20 | 21 | arr = []int{} 22 | expect = 100 23 | 24 | res, err = gollection.New(arr).Fold(100, func(v1, v2 int) int { 25 | return v1 + v2 26 | }).Result() 27 | assert.NoError(err) 28 | assert.Equal(expect, res) 29 | } 30 | 31 | func TestFold_NotSlice(t *testing.T) { 32 | assert := assert.New(t) 33 | _, err := gollection.New("not slice value").Fold(100, func(v1, v2 interface{}) interface{} { 34 | return "" 35 | }).Result() 36 | assert.Error(err) 37 | } 38 | func TestFold_NotFunc(t *testing.T) { 39 | assert := assert.New(t) 40 | _, err := gollection.New([]int{0}).Fold(100, 0).Result() 41 | assert.Error(err) 42 | } 43 | 44 | func TestFold_HavingError(t *testing.T) { 45 | assert := assert.New(t) 46 | _, err := gollection.New("not slice value"). 47 | Fold(100, func(v1, v2 interface{}) interface{} { 48 | return "" 49 | }). 50 | Fold(100, func(v1, v2 interface{}) interface{} { 51 | return "" 52 | }). 53 | Result() 54 | assert.Error(err) 55 | } 56 | 57 | func TestFold_Stream(t *testing.T) { 58 | assert := assert.New(t) 59 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 60 | expect := 155 61 | 62 | res, err := gollection.NewStream(arr).Fold(100, func(v1, v2 int) int { 63 | return v1 + v2 64 | }).Result() 65 | assert.NoError(err) 66 | assert.Equal(expect, res) 67 | 68 | arr = []int{} 69 | expect = 100 70 | 71 | res, err = gollection.NewStream(arr).Fold(100, func(v1, v2 int) int { 72 | return v1 + v2 73 | }).Result() 74 | assert.NoError(err) 75 | assert.Equal(expect, res) 76 | } 77 | func TestFold_Stream_NotSlice(t *testing.T) { 78 | assert := assert.New(t) 79 | _, err := gollection.NewStream("not slice value").Fold(100, func(v1, v2 interface{}) interface{} { 80 | return "" 81 | }).Result() 82 | assert.Error(err) 83 | } 84 | func TestFold_Stream_NotFunc(t *testing.T) { 85 | assert := assert.New(t) 86 | _, err := gollection.NewStream([]int{0}).Fold(100, 0).Result() 87 | assert.Error(err) 88 | } 89 | 90 | func TestFold_Stream_HavingError(t *testing.T) { 91 | assert := assert.New(t) 92 | _, err := gollection.NewStream("not slice value"). 93 | Fold(100, func(v1, v2 interface{}) interface{} { 94 | return "" 95 | }). 96 | Fold(100, func(v1, v2 interface{}) interface{} { 97 | return "" 98 | }). 99 | Result() 100 | assert.Error(err) 101 | } 102 | -------------------------------------------------------------------------------- /gollection.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package gollection provides collection util to any type slices. 3 | */ 4 | package gollection 5 | 6 | import ( 7 | "fmt" 8 | "reflect" 9 | "sync" 10 | ) 11 | 12 | type gollection struct { 13 | slice interface{} 14 | val interface{} 15 | ch chan interface{} 16 | err error 17 | } 18 | 19 | // New returns a gollection instance which can method chain *sequentially* specified by some type of slice. 20 | func New(slice interface{}) *gollection { 21 | return &gollection{ 22 | slice: slice, 23 | } 24 | } 25 | 26 | // Result return a collection processed value and error. 27 | func (g *gollection) Result() (interface{}, error) { 28 | if g.ch != nil { 29 | return g.resultStream() 30 | } 31 | return g.result() 32 | } 33 | 34 | func (g *gollection) result() (interface{}, error) { 35 | if g.val != nil { 36 | return g.val, g.err 37 | } 38 | return g.slice, g.err 39 | } 40 | 41 | func (g *gollection) ResultAs(out interface{}) error { 42 | if g.err != nil { 43 | return g.err 44 | } 45 | 46 | iv := reflect.ValueOf(g.slice) 47 | if g.val != nil { 48 | iv = reflect.ValueOf(g.val) 49 | } 50 | 51 | ov := reflect.ValueOf(out) 52 | if ov.Kind() != reflect.Ptr || iv.Type() != ov.Elem().Type() { 53 | return fmt.Errorf("gollection.ResultAs called with unexpected type %T, expected %s", g.slice, ov.Elem().Type()) 54 | } 55 | 56 | ov.Elem().Set(iv) 57 | return nil 58 | } 59 | 60 | func (g *gollection) resultStream() (interface{}, error) { 61 | var ret reflect.Value 62 | var initialized bool 63 | var err error 64 | 65 | wg := sync.WaitGroup{} 66 | wg.Add(1) 67 | go func(err *error) { 68 | for { 69 | select { 70 | case v, ok := <-g.ch: 71 | if ok { 72 | if e, ok := v.(error); ok { 73 | *err = e 74 | wg.Done() 75 | return 76 | } 77 | if !initialized { 78 | ret = reflect.MakeSlice(v.(reflect.Type), 0, 0) 79 | initialized = true 80 | continue 81 | } 82 | ret = reflect.Append(ret, reflect.ValueOf(v)) 83 | } else { 84 | wg.Done() 85 | return 86 | } 87 | default: 88 | continue 89 | } 90 | } 91 | }(&err) 92 | wg.Wait() 93 | 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | return ret.Interface(), nil 99 | } 100 | 101 | // NewStream returns a gollection instance which can method chain *parallel* specified by some type of slice. 102 | func NewStream(slice interface{}) *gollection { 103 | next := &gollection{ 104 | ch: make(chan interface{}), 105 | } 106 | 107 | sv := reflect.ValueOf(slice) 108 | if sv.Kind() != reflect.Slice { 109 | return &gollection{ 110 | err: fmt.Errorf("gollection.NewStream called with non-slice value of type %T", slice), 111 | } 112 | } 113 | 114 | go func() { 115 | // initialze next stream type 116 | next.ch <- sv.Type() 117 | 118 | for i := 0; i < sv.Len(); i++ { 119 | next.ch <- sv.Index(i).Interface() 120 | } 121 | close(next.ch) 122 | }() 123 | return next 124 | } 125 | -------------------------------------------------------------------------------- /gollection_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/azihsoyn/gollection" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestResultAs_UnexpectedType(t *testing.T) { 11 | assert := assert.New(t) 12 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 13 | res := []string{} 14 | 15 | err := gollection.New(arr).ResultAs(&res) 16 | assert.Error(err) 17 | } 18 | -------------------------------------------------------------------------------- /map.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import "reflect" 4 | 5 | func (g *gollection) Map(f /* func(v ) */ interface{}) *gollection { 6 | if g.err != nil { 7 | return &gollection{err: g.err} 8 | } 9 | 10 | if g.ch != nil { 11 | return g.mapStream(f) 12 | } 13 | 14 | return g.map_(f) 15 | } 16 | 17 | func (g *gollection) map_(f interface{}) *gollection { 18 | sv, err := g.validateSlice("Map") 19 | if err != nil { 20 | return &gollection{err: err} 21 | } 22 | 23 | funcValue, funcType, err := g.validateMapFunc(f) 24 | if err != nil { 25 | return &gollection{err: err} 26 | } 27 | 28 | resultSliceType := reflect.SliceOf(funcType.Out(0)) 29 | ret := reflect.MakeSlice(resultSliceType, 0, sv.Len()) 30 | 31 | // avoid "panic: reflect: call of reflect.Value.Interface on zero Value" 32 | // see https://github.com/azihsoyn/gollection/issues/7 33 | if sv.Len() == 0 { 34 | return &gollection{ 35 | slice: ret.Interface(), 36 | } 37 | } 38 | 39 | for i := 0; i < sv.Len(); i++ { 40 | v := processMapFunc(funcValue, sv.Index(i)) 41 | ret = reflect.Append(ret, v) 42 | } 43 | 44 | return &gollection{ 45 | slice: ret.Interface(), 46 | } 47 | 48 | } 49 | 50 | func (g *gollection) mapStream(f interface{}) *gollection { 51 | next := &gollection{ 52 | ch: make(chan interface{}), 53 | } 54 | 55 | funcValue, funcType, err := g.validateMapFunc(f) 56 | if err != nil { 57 | return &gollection{err: err} 58 | } 59 | 60 | var initialized bool 61 | go func() { 62 | for { 63 | select { 64 | case v, ok := <-g.ch: 65 | if ok { 66 | // initialize next stream type 67 | if !initialized { 68 | next.ch <- reflect.SliceOf(funcType.Out(0)) 69 | initialized = true 70 | continue 71 | } 72 | 73 | v := processMapFunc(funcValue, reflect.ValueOf(v)).Interface() 74 | next.ch <- v 75 | } else { 76 | close(next.ch) 77 | return 78 | } 79 | default: 80 | continue 81 | } 82 | } 83 | }() 84 | return next 85 | } 86 | -------------------------------------------------------------------------------- /map_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/azihsoyn/gollection" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestMap(t *testing.T) { 12 | assert := assert.New(t) 13 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 14 | expect := []int{2, 4, 6, 8, 10, 12, 14, 16, 18, 20} 15 | 16 | res, err := gollection.New(arr).Map(func(v int) int { 17 | return v * 2 18 | }).Result() 19 | assert.NoError(err) 20 | assert.Equal(expect, res) 21 | } 22 | 23 | func TestMap_WithCast(t *testing.T) { 24 | assert := assert.New(t) 25 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 26 | expect := []string{"2", "4", "6", "8", "10", "12", "14", "16", "18", "20"} 27 | 28 | res, err := gollection.New(arr).Map(func(v int) string { 29 | return fmt.Sprintf("%d", v*2) 30 | }).Result() 31 | assert.NoError(err) 32 | assert.Equal(expect, res) 33 | } 34 | 35 | func TestMap_EmptySlice(t *testing.T) { 36 | assert := assert.New(t) 37 | _, err := gollection.New([]int{}).Map(func(v interface{}) interface{} { 38 | return "" 39 | }).Result() 40 | assert.NoError(err) 41 | } 42 | func TestMap_NotSlice(t *testing.T) { 43 | assert := assert.New(t) 44 | _, err := gollection.New("not slice value").Map(func(v interface{}) interface{} { 45 | return "" 46 | }).Result() 47 | assert.Error(err) 48 | } 49 | func TestMap_NotFunc(t *testing.T) { 50 | assert := assert.New(t) 51 | _, err := gollection.New([]int{}).Map(0).Result() 52 | assert.Error(err) 53 | } 54 | 55 | func TestMap_HavingError(t *testing.T) { 56 | assert := assert.New(t) 57 | _, err := gollection.New("not slice value"). 58 | Map(func(v interface{}) interface{} { 59 | return "" 60 | }). 61 | Map(func(v interface{}) interface{} { 62 | return "" 63 | }). 64 | Result() 65 | assert.Error(err) 66 | } 67 | 68 | func TestMap_Stream(t *testing.T) { 69 | assert := assert.New(t) 70 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 71 | expect := []int{2, 4, 6, 8, 10, 12, 14, 16, 18, 20} 72 | 73 | res, err := gollection.NewStream(arr).Map(func(v int) int { 74 | return v * 2 75 | }).Result() 76 | assert.NoError(err) 77 | assert.Equal(expect, res) 78 | 79 | gollection.NewStream(arr).Map(func(v int) int { 80 | return v * 2 81 | }) 82 | } 83 | 84 | func TestMap_Stream_WithCast(t *testing.T) { 85 | assert := assert.New(t) 86 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 87 | expect := []string{"2", "4", "6", "8", "10", "12", "14", "16", "18", "20"} 88 | 89 | res, err := gollection.NewStream(arr).Map(func(v int) string { 90 | return fmt.Sprintf("%d", v*2) 91 | }).Result() 92 | assert.NoError(err) 93 | assert.Equal(expect, res) 94 | } 95 | 96 | func TestMap_Stream_EmptySlice(t *testing.T) { 97 | assert := assert.New(t) 98 | _, err := gollection.NewStream([]int{}).Map(func(v interface{}) interface{} { 99 | return "" 100 | }).Result() 101 | assert.NoError(err) 102 | } 103 | func TestMap_Stream_NotSlice(t *testing.T) { 104 | assert := assert.New(t) 105 | _, err := gollection.NewStream("not slice value").Map(func(v interface{}) interface{} { 106 | return "" 107 | }).Result() 108 | assert.Error(err) 109 | } 110 | func TestMap_Stream_NotFunc(t *testing.T) { 111 | assert := assert.New(t) 112 | _, err := gollection.NewStream([]int{}).Map(0).Result() 113 | assert.Error(err) 114 | } 115 | 116 | func TestMap_Stream_HavingError(t *testing.T) { 117 | assert := assert.New(t) 118 | _, err := gollection.NewStream("not slice value"). 119 | Map(func(v interface{}) interface{} { 120 | return "" 121 | }). 122 | Map(func(v interface{}) interface{} { 123 | return "" 124 | }). 125 | Result() 126 | assert.Error(err) 127 | } 128 | -------------------------------------------------------------------------------- /process.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import ( 4 | "reflect" 5 | "sort" 6 | 7 | "go4.org/reflectutil" 8 | ) 9 | 10 | func processDistinct(v interface{}, m map[interface{}]bool) bool { 11 | if _, ok := m[v]; !ok { 12 | m[v] = true 13 | return true 14 | } 15 | return false 16 | } 17 | 18 | func processDistinctBy(fv, v reflect.Value, m map[interface{}]bool) bool { 19 | id := fv.Call([]reflect.Value{v})[0].Interface() 20 | if _, ok := m[id]; !ok { 21 | m[id] = true 22 | return true 23 | } 24 | return false 25 | } 26 | 27 | func processFilter(fv, v reflect.Value) bool { 28 | return fv.Call([]reflect.Value{v})[0].Interface().(bool) 29 | } 30 | 31 | func processMapFunc(fv, arg reflect.Value) reflect.Value { 32 | return fv.Call([]reflect.Value{arg})[0] 33 | } 34 | 35 | func processReduceFunc(fv, arg1, arg2 reflect.Value) reflect.Value { 36 | return fv.Call([]reflect.Value{arg1, arg2})[0] 37 | } 38 | 39 | func processSort(fv, ret reflect.Value) { 40 | less := func(i, j int) bool { 41 | return fv.Call([]reflect.Value{ret.Index(i), ret.Index(j)})[0].Interface().(bool) 42 | } 43 | 44 | sort.Sort(&funcs{ 45 | length: ret.Len(), 46 | less: less, 47 | swap: reflectutil.Swapper(ret.Interface()), 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /reduce.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "sync" 7 | ) 8 | 9 | func (g *gollection) Reduce(f /* func(v1, v2 ) */ interface{}) *gollection { 10 | if g.err != nil { 11 | return &gollection{err: g.err} 12 | } 13 | 14 | if g.ch != nil { 15 | return g.reduceStream(f) 16 | } 17 | 18 | return g.reduce(f) 19 | } 20 | 21 | func (g *gollection) reduce(f interface{}) *gollection { 22 | sv, err := g.validateSlice("Reduce") 23 | if err != nil { 24 | return &gollection{err: err} 25 | } 26 | 27 | if sv.Len() == 0 { 28 | return &gollection{ 29 | slice: nil, 30 | err: fmt.Errorf("gollection.Reduce called with empty slice of type %T", g.slice), 31 | } 32 | } else if sv.Len() == 1 { 33 | return &gollection{ 34 | val: sv.Index(0).Interface(), 35 | } 36 | } 37 | 38 | funcValue, _, err := g.validateReduceFunc(f) 39 | if err != nil { 40 | return &gollection{err: err} 41 | } 42 | 43 | ret := sv.Index(0).Interface() 44 | for i := 1; i < sv.Len(); i++ { 45 | v1 := reflect.ValueOf(ret) 46 | v2 := sv.Index(i) 47 | ret = processReduceFunc(funcValue, v1, v2).Interface() 48 | } 49 | 50 | return &gollection{ 51 | val: ret, 52 | } 53 | } 54 | 55 | func (g *gollection) reduceStream(f interface{}) *gollection { 56 | funcValue, _, err := g.validateReduceFunc(f) 57 | if err != nil { 58 | return &gollection{err: err} 59 | } 60 | 61 | var ret interface{} 62 | wg := sync.WaitGroup{} 63 | wg.Add(1) 64 | go func(wg *sync.WaitGroup, ret *interface{}, err *error) { 65 | var initialized bool 66 | var skippedFirst bool 67 | var itemNum int 68 | var currentType reflect.Type 69 | 70 | for { 71 | select { 72 | case v, ok := <-g.ch: 73 | if ok { 74 | // skip first item(reflect.Type) 75 | if !skippedFirst { 76 | skippedFirst = true 77 | currentType = v.(reflect.Type) 78 | continue 79 | } 80 | 81 | if !initialized { 82 | itemNum++ 83 | *ret = reflect.ValueOf(v).Interface() 84 | initialized = true 85 | continue 86 | } 87 | 88 | v1 := reflect.ValueOf(*ret) 89 | v2 := reflect.ValueOf(v) 90 | *ret = processReduceFunc(funcValue, v1, v2).Interface() 91 | } else { 92 | if itemNum == 0 { 93 | *err = fmt.Errorf("gollection.Reduce called with empty slice of type %s", currentType) 94 | } 95 | (*wg).Done() 96 | return 97 | } 98 | default: 99 | continue 100 | } 101 | } 102 | }(&wg, &ret, &err) 103 | wg.Wait() 104 | 105 | if err != nil { 106 | return &gollection{ 107 | err: err, 108 | } 109 | } 110 | 111 | return &gollection{ 112 | val: ret, 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /reduce_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/azihsoyn/gollection" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestReduce(t *testing.T) { 11 | assert := assert.New(t) 12 | 13 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 14 | expect := 55 15 | 16 | res, err := gollection.New(arr).Reduce(func(v1, v2 int) int { 17 | return v1 + v2 18 | }).Result() 19 | assert.NoError(err) 20 | assert.Equal(expect, res) 21 | 22 | var ret int 23 | err = gollection.New(arr).Reduce(func(v1, v2 int) int { 24 | return v1 + v2 25 | }).ResultAs(&ret) 26 | assert.NoError(err) 27 | assert.Equal(expect, ret) 28 | 29 | arr = []int{1} 30 | expect = 1 31 | 32 | res, err = gollection.New(arr).Reduce(func(v1, v2 int) int { 33 | return v1 + v2 34 | }).Result() 35 | assert.NoError(err) 36 | assert.Equal(expect, res) 37 | } 38 | func TestReduce_NotSlice(t *testing.T) { 39 | assert := assert.New(t) 40 | _, err := gollection.New("not slice value").Reduce(func(v1, v2 interface{}) interface{} { 41 | return "" 42 | }).Result() 43 | assert.Error(err) 44 | } 45 | func TestReduce_NotFunc(t *testing.T) { 46 | assert := assert.New(t) 47 | _, err := gollection.New([]int{0, 0, 0}).Reduce(0).Result() 48 | assert.Error(err) 49 | } 50 | 51 | func TestReduce_EmptySlice(t *testing.T) { 52 | assert := assert.New(t) 53 | _, err := gollection.New([]int{}).Reduce(func(v1, v2 interface{}) interface{} { 54 | return "" 55 | }).Result() 56 | assert.Error(err) 57 | } 58 | 59 | func TestReduce_HavingError(t *testing.T) { 60 | assert := assert.New(t) 61 | _, err := gollection.New("not slice value"). 62 | Reduce(func(v1, v2 interface{}) interface{} { 63 | return "" 64 | }). 65 | Reduce(func(v1, v2 interface{}) interface{} { 66 | return "" 67 | }). 68 | Result() 69 | assert.Error(err) 70 | } 71 | 72 | func TestReduce_Stream(t *testing.T) { 73 | assert := assert.New(t) 74 | 75 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 76 | expect := 55 77 | 78 | res, err := gollection.NewStream(arr).Reduce(func(v1, v2 int) int { 79 | return v1 + v2 80 | }).Result() 81 | assert.NoError(err) 82 | assert.Equal(expect, res) 83 | 84 | arr = []int{1} 85 | expect = 1 86 | 87 | res, err = gollection.NewStream(arr).Reduce(func(v1, v2 int) int { 88 | return v1 + v2 89 | }).Result() 90 | assert.NoError(err) 91 | assert.Equal(expect, res) 92 | } 93 | func TestReduce_Stream_NotSlice(t *testing.T) { 94 | assert := assert.New(t) 95 | _, err := gollection.NewStream("not slice value").Reduce(func(v1, v2 interface{}) interface{} { 96 | return "" 97 | }).Result() 98 | assert.Error(err) 99 | } 100 | func TestReduce_Stream_NotFunc(t *testing.T) { 101 | assert := assert.New(t) 102 | _, err := gollection.NewStream([]int{0, 0, 0}).Reduce(0).Result() 103 | assert.Error(err) 104 | } 105 | 106 | func TestReduce_Stream_EmptySlice(t *testing.T) { 107 | assert := assert.New(t) 108 | _, err := gollection.NewStream([]int{}).Reduce(func(v1, v2 interface{}) interface{} { 109 | return "" 110 | }).Result() 111 | assert.Error(err) 112 | } 113 | 114 | func TestReduce_Stream_HavingError(t *testing.T) { 115 | assert := assert.New(t) 116 | _, err := gollection.NewStream("not slice value"). 117 | Reduce(func(v1, v2 interface{}) interface{} { 118 | return "" 119 | }). 120 | Reduce(func(v1, v2 interface{}) interface{} { 121 | return "" 122 | }). 123 | Result() 124 | assert.Error(err) 125 | } 126 | -------------------------------------------------------------------------------- /skip.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func (g *gollection) Skip(n int) *gollection { 9 | if g.err != nil { 10 | return &gollection{err: g.err} 11 | } 12 | 13 | if g.ch != nil { 14 | return g.skipStream(n) 15 | } 16 | 17 | return g.skip(n) 18 | } 19 | 20 | func (g *gollection) skip(n int) *gollection { 21 | sv, err := g.validateSlice("Take") 22 | if err != nil { 23 | return &gollection{err: err} 24 | } 25 | 26 | if n < 0 { 27 | return &gollection{err: fmt.Errorf("gollection.Skip called with invalid argument. should be larger than 0")} 28 | } 29 | 30 | limit := sv.Len() 31 | start := n 32 | if limit < start { 33 | start = limit 34 | } 35 | 36 | ret := reflect.MakeSlice(sv.Type(), 0, limit-start) 37 | 38 | for i := start; i < limit; i++ { 39 | ret = reflect.Append(ret, sv.Index(i)) 40 | } 41 | 42 | return &gollection{ 43 | slice: ret.Interface(), 44 | } 45 | } 46 | 47 | func (g *gollection) skipStream(n int) *gollection { 48 | next := &gollection{ 49 | ch: make(chan interface{}), 50 | } 51 | 52 | var initialized bool 53 | go func() { 54 | i := 0 55 | for { 56 | select { 57 | case v, ok := <-g.ch: 58 | // initialize next stream type 59 | if ok && !initialized { 60 | next.ch <- v 61 | initialized = true 62 | continue 63 | } 64 | 65 | if ok { 66 | i++ 67 | if n < i { 68 | next.ch <- v 69 | } 70 | } else { 71 | close(next.ch) 72 | return 73 | } 74 | default: 75 | continue 76 | } 77 | } 78 | }() 79 | return next 80 | } 81 | -------------------------------------------------------------------------------- /skip_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/azihsoyn/gollection" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestSkip(t *testing.T) { 12 | assert := assert.New(t) 13 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 14 | expect := []int{4, 5, 6, 7, 8, 9, 10} 15 | 16 | res, err := gollection.New(arr).Skip(3).Result() 17 | assert.NoError(err) 18 | assert.Equal(expect, res) 19 | 20 | expect = []int{} 21 | res, err = gollection.New(arr).Skip(30).Result() 22 | assert.NoError(err) 23 | assert.Equal(expect, res) 24 | } 25 | 26 | func TestSkip_NotSlice(t *testing.T) { 27 | assert := assert.New(t) 28 | _, err := gollection.New("not slice value").Skip(0).Result() 29 | assert.Error(err) 30 | } 31 | 32 | func TestSkip_InvalidArgument(t *testing.T) { 33 | assert := assert.New(t) 34 | _, err := gollection.New([]int{1, 2, 3}).Skip(-1).Result() 35 | assert.Error(err) 36 | } 37 | 38 | func TestSkip_HavingError(t *testing.T) { 39 | assert := assert.New(t) 40 | _, err := gollection.New("not slice value").Skip(0).Skip(0).Result() 41 | assert.Error(err) 42 | } 43 | 44 | func TestSkip_Stream(t *testing.T) { 45 | assert := assert.New(t) 46 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 47 | expect := []int{4, 5, 6, 7, 8, 9, 10} 48 | 49 | res, err := gollection.NewStream(arr).Skip(3).Result() 50 | assert.NoError(err) 51 | assert.Equal(expect, res) 52 | 53 | expect = []int{} 54 | res, err = gollection.NewStream(arr).Skip(30).Result() 55 | assert.NoError(err) 56 | assert.Equal(expect, res) 57 | 58 | gollection.NewStream(arr).Filter(func(v int) bool { 59 | time.Sleep(1) 60 | return true 61 | }).Skip(100) 62 | } 63 | 64 | func TestSkip_Stream_NotSlice(t *testing.T) { 65 | assert := assert.New(t) 66 | _, err := gollection.NewStream("not slice value").Skip(0).Result() 67 | assert.Error(err) 68 | } 69 | 70 | func TestSkip_Stream_HavingError(t *testing.T) { 71 | assert := assert.New(t) 72 | _, err := gollection.NewStream("not slice value").Skip(0).Skip(0).Result() 73 | assert.Error(err) 74 | } 75 | -------------------------------------------------------------------------------- /sort.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import ( 4 | "reflect" 5 | "sync" 6 | ) 7 | 8 | func (g *gollection) SortBy(f /* func(v1, v2 ) bool */ interface{}) *gollection { 9 | if g.err != nil { 10 | return &gollection{err: g.err} 11 | } 12 | 13 | if g.ch != nil { 14 | return g.sortByStream(f) 15 | } 16 | 17 | return g.sortBy(f) 18 | } 19 | 20 | func (g *gollection) sortBy(f interface{}) *gollection { 21 | sv, err := g.validateSlice("SortBy") 22 | if err != nil { 23 | return &gollection{err: err} 24 | } 25 | 26 | ret := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Cap()) 27 | reflect.Copy(ret, sv) 28 | 29 | funcValue, _, err := g.validateSortByFunc(f) 30 | if err != nil { 31 | return &gollection{err: err} 32 | } 33 | 34 | processSort(funcValue, ret) 35 | 36 | return &gollection{ 37 | slice: ret.Interface(), 38 | err: nil, 39 | } 40 | } 41 | 42 | func (g *gollection) sortByStream(f interface{}) *gollection { 43 | next := &gollection{ 44 | ch: make(chan interface{}), 45 | } 46 | 47 | funcValue, _, err := g.validateSortByFunc(f) 48 | if err != nil { 49 | return &gollection{err: err} 50 | } 51 | 52 | var ret reflect.Value 53 | var initialized bool 54 | var skippedFirst bool 55 | var currentType reflect.Type 56 | 57 | wg := sync.WaitGroup{} 58 | wg.Add(1) 59 | go func(wg *sync.WaitGroup, currentType *reflect.Type) { 60 | for { 61 | select { 62 | case v, ok := <-g.ch: 63 | if ok { 64 | if !skippedFirst { 65 | skippedFirst = true 66 | *currentType = v.(reflect.Type) 67 | continue 68 | } 69 | 70 | // initialze next stream type 71 | if !initialized { 72 | ret = reflect.MakeSlice(reflect.SliceOf(reflect.ValueOf(v).Type()), 0, 0) 73 | initialized = true 74 | } 75 | 76 | ret = reflect.Append(ret, reflect.ValueOf(v)) 77 | } else { 78 | wg.Done() 79 | return 80 | } 81 | default: 82 | continue 83 | } 84 | } 85 | }(&wg, ¤tType) 86 | wg.Wait() 87 | 88 | processSort(funcValue, ret) 89 | 90 | go func() { 91 | // initialze next stream type 92 | next.ch <- currentType 93 | 94 | for i := 0; i < ret.Len(); i++ { 95 | next.ch <- ret.Index(i).Interface() 96 | } 97 | close(next.ch) 98 | }() 99 | return next 100 | } 101 | 102 | type funcs struct { 103 | length int 104 | less func(i, j int) bool 105 | swap func(i, j int) 106 | } 107 | 108 | func (f *funcs) Len() int { return f.length } 109 | func (f *funcs) Less(i, j int) bool { return f.less(i, j) } 110 | func (f *funcs) Swap(i, j int) { 111 | f.swap(i, j) 112 | } 113 | -------------------------------------------------------------------------------- /sort_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/azihsoyn/gollection" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestSortBy(t *testing.T) { 11 | assert := assert.New(t) 12 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} 13 | expect := []int{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10} 14 | original := make([]int, len(arr)) 15 | copy(original, arr) 16 | 17 | res, err := gollection.New(arr).SortBy(func(v1, v2 int) bool { 18 | return v1 < v2 19 | }).Result() 20 | 21 | assert.NoError(err) 22 | assert.Equal(expect, res) 23 | // check not changed 24 | assert.Equal(original, arr) 25 | } 26 | func TestSortBy_NotSlice(t *testing.T) { 27 | assert := assert.New(t) 28 | _, err := gollection.New("not slice value").SortBy(func(v1, v2 interface{}) bool { 29 | return false 30 | }).Result() 31 | assert.Error(err) 32 | } 33 | func TestSortBy_NotFunc(t *testing.T) { 34 | assert := assert.New(t) 35 | _, err := gollection.New([]int{0, 0, 0}).SortBy(0).Result() 36 | assert.Error(err) 37 | } 38 | 39 | func TestSortBy_HavingError(t *testing.T) { 40 | assert := assert.New(t) 41 | _, err := gollection.New("not slice value"). 42 | SortBy(func(v1, v2 interface{}) bool { 43 | return false 44 | }). 45 | SortBy(func(v1, v2 interface{}) bool { 46 | return false 47 | }). 48 | Result() 49 | assert.Error(err) 50 | } 51 | 52 | func TestSortBy_Stream(t *testing.T) { 53 | assert := assert.New(t) 54 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} 55 | expect := []int{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10} 56 | original := make([]int, len(arr)) 57 | copy(original, arr) 58 | 59 | res, err := gollection.NewStream(arr).SortBy(func(v1, v2 int) bool { 60 | return v1 < v2 61 | }).Result() 62 | 63 | assert.NoError(err) 64 | assert.Equal(expect, res) 65 | // check not changed 66 | assert.Equal(original, arr) 67 | } 68 | func TestSortBy_Stream_NotSlice(t *testing.T) { 69 | assert := assert.New(t) 70 | _, err := gollection.NewStream("not slice value").SortBy(func(v1, v2 interface{}) bool { 71 | return false 72 | }).Result() 73 | assert.Error(err) 74 | } 75 | func TestSortBy_Stream_NotFunc(t *testing.T) { 76 | assert := assert.New(t) 77 | _, err := gollection.NewStream([]int{0, 0, 0}).SortBy(0).Result() 78 | assert.Error(err) 79 | } 80 | 81 | func TestSortBy_Stream_HavingError(t *testing.T) { 82 | assert := assert.New(t) 83 | _, err := gollection.NewStream("not slice value"). 84 | SortBy(func(v1, v2 interface{}) bool { 85 | return false 86 | }). 87 | SortBy(func(v1, v2 interface{}) bool { 88 | return false 89 | }). 90 | Result() 91 | assert.Error(err) 92 | } 93 | -------------------------------------------------------------------------------- /take.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import "reflect" 4 | 5 | func (g *gollection) Take(n int) *gollection { 6 | if g.err != nil { 7 | return &gollection{err: g.err} 8 | } 9 | 10 | if g.ch != nil { 11 | return g.takeStream(n) 12 | } 13 | 14 | return g.take(n) 15 | } 16 | 17 | func (g *gollection) take(n int) *gollection { 18 | sv, err := g.validateSlice("Take") 19 | if err != nil { 20 | return &gollection{err: err} 21 | } 22 | 23 | limit := sv.Len() 24 | if n < limit { 25 | limit = n 26 | } 27 | 28 | ret := reflect.MakeSlice(sv.Type(), 0, sv.Len()) 29 | 30 | for i := 0; i < limit; i++ { 31 | ret = reflect.Append(ret, sv.Index(i)) 32 | } 33 | 34 | return &gollection{ 35 | slice: ret.Interface(), 36 | } 37 | } 38 | 39 | func (g *gollection) takeStream(n int) *gollection { 40 | next := &gollection{ 41 | ch: make(chan interface{}), 42 | } 43 | 44 | var initialized bool 45 | go func() { 46 | i := 0 47 | for { 48 | select { 49 | case v, ok := <-g.ch: 50 | // initialize next stream type 51 | if ok && !initialized { 52 | next.ch <- v 53 | initialized = true 54 | continue 55 | } 56 | 57 | if ok && i < n { 58 | next.ch <- v 59 | i++ 60 | } else { 61 | close(next.ch) 62 | return 63 | } 64 | default: 65 | continue 66 | } 67 | } 68 | }() 69 | return next 70 | } 71 | -------------------------------------------------------------------------------- /take_test.go: -------------------------------------------------------------------------------- 1 | package gollection_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/azihsoyn/gollection" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestTake(t *testing.T) { 12 | assert := assert.New(t) 13 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 14 | expect := []int{1, 2, 3} 15 | 16 | res, err := gollection.New(arr).Take(3).Result() 17 | assert.NoError(err) 18 | assert.Equal(expect, res) 19 | 20 | expect = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 21 | res, err = gollection.New(arr).Take(30).Result() 22 | assert.NoError(err) 23 | assert.Equal(expect, res) 24 | } 25 | 26 | func TestTake_NotSlice(t *testing.T) { 27 | assert := assert.New(t) 28 | _, err := gollection.New("not slice value").Take(0).Result() 29 | assert.Error(err) 30 | } 31 | 32 | func TestTake_HavingError(t *testing.T) { 33 | assert := assert.New(t) 34 | _, err := gollection.New("not slice value").Take(0).Take(0).Result() 35 | assert.Error(err) 36 | } 37 | 38 | func TestTake_Stream(t *testing.T) { 39 | assert := assert.New(t) 40 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 41 | expect := []int{1, 2, 3} 42 | 43 | res, err := gollection.NewStream(arr).Take(3).Result() 44 | assert.NoError(err) 45 | assert.Equal(expect, res) 46 | 47 | expect = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 48 | res, err = gollection.NewStream(arr).Take(30).Result() 49 | assert.NoError(err) 50 | assert.Equal(expect, res) 51 | 52 | gollection.NewStream(arr).Filter(func(v int) bool { 53 | time.Sleep(1) 54 | return true 55 | }).Take(100) 56 | } 57 | 58 | func TestTake_Stream_NotSlice(t *testing.T) { 59 | assert := assert.New(t) 60 | _, err := gollection.NewStream("not slice value").Take(0).Result() 61 | assert.Error(err) 62 | } 63 | 64 | func TestTake_Stream_HavingError(t *testing.T) { 65 | assert := assert.New(t) 66 | _, err := gollection.NewStream("not slice value").Take(0).Take(0).Result() 67 | assert.Error(err) 68 | } 69 | -------------------------------------------------------------------------------- /validate.go: -------------------------------------------------------------------------------- 1 | package gollection 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func (g *gollection) validateSlice(funcName string) (reflect.Value, error) { 9 | sv := reflect.ValueOf(g.slice) 10 | if sv.Kind() != reflect.Slice { 11 | return reflect.Value{}, fmt.Errorf("gollection.%s called with non-slice value of type %T", funcName, g.slice) 12 | } 13 | return sv, nil 14 | } 15 | 16 | func (g *gollection) validateSliceOfSlice(funcName string) (reflect.Type, error) { 17 | currentType := reflect.TypeOf(g.slice).Elem() 18 | if currentType.Kind() != reflect.Slice { 19 | return nil, fmt.Errorf("gollection.%s called with non-slice value of type %T", funcName, g.slice) 20 | } 21 | return currentType, nil 22 | } 23 | 24 | func (g *gollection) validateDistinctByFunc(f interface{}) (reflect.Value, reflect.Type, error) { 25 | funcValue := reflect.ValueOf(f) 26 | funcType := funcValue.Type() 27 | if funcType.Kind() != reflect.Func || funcType.NumIn() != 1 || funcType.NumOut() != 1 { 28 | return reflect.Value{}, nil, fmt.Errorf("gollection.DistinctBy called with invalid func. required func(in ) out but supplied %v", funcType) 29 | } 30 | return funcValue, funcType, nil 31 | } 32 | 33 | func (g *gollection) validateFilterFunc(f interface{}) (reflect.Value, reflect.Type, error) { 34 | funcValue := reflect.ValueOf(f) 35 | funcType := funcValue.Type() 36 | if funcType.Kind() != reflect.Func || 37 | funcType.NumIn() != 1 || 38 | funcType.NumOut() != 1 || 39 | funcType.Out(0).Kind() != reflect.Bool { 40 | return reflect.Value{}, nil, fmt.Errorf("gollection.Filter called with invalid func. required func(in ) bool but supplied %v", funcType) 41 | } 42 | return funcValue, funcType, nil 43 | } 44 | 45 | func (g *gollection) validateFlatMapFunc(f interface{}) (reflect.Value, reflect.Type, error) { 46 | funcValue := reflect.ValueOf(f) 47 | funcType := funcValue.Type() 48 | if funcType.Kind() != reflect.Func || funcType.NumIn() != 1 || funcType.NumOut() != 1 { 49 | return reflect.Value{}, nil, fmt.Errorf("gollection.FlatMap called with invalid func. required func(in ) out but supplied %v", funcType) 50 | } 51 | return funcValue, funcType, nil 52 | } 53 | 54 | func (g *gollection) validateFoldFunc(f interface{}) (reflect.Value, reflect.Type, error) { 55 | funcValue := reflect.ValueOf(f) 56 | funcType := funcValue.Type() 57 | if funcType.Kind() != reflect.Func || 58 | funcType.NumIn() != 2 || 59 | funcType.NumOut() != 1 { 60 | return reflect.Value{}, nil, fmt.Errorf("gollection.Fold called with invalid func. required func(in1, in2 ) out but supplied %v", funcType) 61 | } 62 | return funcValue, funcType, nil 63 | } 64 | 65 | func (g *gollection) validateMapFunc(f interface{}) (reflect.Value, reflect.Type, error) { 66 | funcValue := reflect.ValueOf(f) 67 | funcType := funcValue.Type() 68 | if funcType.Kind() != reflect.Func || 69 | funcType.NumIn() != 1 || 70 | funcType.NumOut() != 1 { 71 | return reflect.Value{}, nil, fmt.Errorf("gollection.Map called with invalid func. required func(in ) out but supplied %v", funcType) 72 | } 73 | return funcValue, funcType, nil 74 | } 75 | 76 | func (g *gollection) validateReduceFunc(f interface{}) (reflect.Value, reflect.Type, error) { 77 | funcValue := reflect.ValueOf(f) 78 | funcType := funcValue.Type() 79 | if funcType.Kind() != reflect.Func || 80 | funcType.NumIn() != 2 || 81 | funcType.NumOut() != 1 { 82 | return reflect.Value{}, nil, fmt.Errorf("gollection.Reduce called with invalid func. required func(in1, in2 ) out but supplied %v", funcType) 83 | } 84 | return funcValue, funcType, nil 85 | } 86 | 87 | func (g *gollection) validateSortByFunc(f interface{}) (reflect.Value, reflect.Type, error) { 88 | funcValue := reflect.ValueOf(f) 89 | funcType := funcValue.Type() 90 | if funcType.Kind() != reflect.Func || 91 | funcType.NumIn() != 2 || 92 | funcType.NumOut() != 1 || 93 | funcType.Out(0).Kind() != reflect.Bool { 94 | return reflect.Value{}, nil, fmt.Errorf("gollection.SortBy called with invalid func. required func(in1, in2 ) bool but supplied %v", funcType) 95 | } 96 | return funcValue, funcType, nil 97 | } 98 | --------------------------------------------------------------------------------