├── .github └── workflows │ ├── lint.yml │ └── tests.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── channel.go ├── channel_test.go ├── cmd └── generate_tuples.go ├── condition.go ├── condition_example_test.go ├── constraints.go ├── error_example.go ├── error_example_test.go ├── find.go ├── find_test.go ├── func.go ├── func_example_test.go ├── func_test.go ├── go.mod ├── go.sum ├── intersect.go ├── intersect_test.go ├── map.go ├── map_example_test.go ├── math.go ├── math_example_test.go ├── math_test.go ├── set.go ├── slice.go ├── slice_benchmark_test.go ├── slice_example_test.go ├── slice_fuzz_test.go ├── slice_test.go ├── string.go ├── string_example_test.go ├── string_test.go ├── toolbox_test.go ├── tuples.go ├── tuples_example_test.go ├── type_manipulation.go └── type_manipulation_example_test.go /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | golangci: 9 | name: lint 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo 'start lint...' 13 | - uses: actions/setup-go@v4 14 | with: 15 | go-version: '1.20' 16 | - uses: actions/checkout@v2 17 | - name: golangci-lint 18 | uses: golangci/golangci-lint-action@v3 19 | with: 20 | args: --timeout 120s --max-same-issues 50 21 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | tags: 6 | branches: 7 | pull_request: 8 | 9 | jobs: 10 | 11 | test: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v4 18 | with: 19 | go-version: '1.20' 20 | 21 | - name: Test 22 | run: make test 23 | 24 | - name: Coverage 25 | run: make coverage 26 | 27 | - name: Codecov 28 | uses: codecov/codecov-action@v3 29 | with: 30 | token: ${{ secrets.CODECOV_TOKEN }} 31 | file: ./cover.out 32 | flags: unittests 33 | verbose: true 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/go 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=go 3 | 4 | ### Go ### 5 | # If you prefer the allow list template instead of the deny list, see community template: 6 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 7 | # 8 | # Binaries for programs and plugins 9 | *.exe 10 | *.exe~ 11 | *.dll 12 | *.so 13 | *.dylib 14 | 15 | # Test binary, built with `go test -c` 16 | *.test 17 | 18 | # Output of the go coverage tool, specifically when used with LiteIDE 19 | *.out 20 | 21 | # Dependency directories (remove the comment below to include it) 22 | # vendor/ 23 | 24 | # Go workspace file 25 | go.work 26 | 27 | # End of https://www.toptal.com/developers/gitignore/api/go 28 | 29 | .idea/ 30 | 31 | cover.html 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Samuel Berthe 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. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | 3 | BIN=go 4 | 5 | test: 6 | ${BIN} test -race -v ./... 7 | watch-test: 8 | reflex -t 50ms -s -- sh -c '${BIN} test -race -v ./...' 9 | 10 | coverage: 11 | ${BIN} test -v -coverprofile=cover.out -covermode=atomic . 12 | ${BIN} tool cover -html=cover.out -o cover.html 13 | 14 | bench: 15 | ${BIN} test -benchmem -count 3 -bench ./... 16 | watch-bench: 17 | reflex -t 50ms -s -- sh -c '${BIN} test -benchmem -count 3 -bench ./...' 18 | 19 | tools: 20 | ${BIN} install github.com/cespare/reflex@latest 21 | # ${BIN} install github.com/rakyll/gotest@latest 22 | # ${BIN} install github.com/psampaz/go-mod-outdated@latest 23 | # ${BIN} install github.com/jondot/goweight@latest 24 | ${BIN} install github.com/golangci/golangci-lint/cmd/golangci-lint@latest 25 | # ${BIN} get -t -u golang.org/x/tools/cmd/cover 26 | # ${BIN} install github.com/sonatype-nexus-community/nancy@latest 27 | # ${BIN} mod tidy 28 | 29 | lint: 30 | golangci-lint run --timeout 60s --max-same-issues 50 ./... 31 | lint-fix: 32 | golangci-lint run --timeout 60s --max-same-issues 50 --fix ./... 33 | 34 | generate: 35 | go run cmd/generate_tuples.go -output tuples.go -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # toolbox - Iterate over slices, maps, channels... 2 | 3 | [![tag](https://img.shields.io/github/tag/wangliliwang/toolbox.svg)](https://github.com/wangliliwang/toolbox/releases) 4 | ![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.20-%23007d9c) 5 | ![Build Status](https://github.com/wangliliwang/toolbox/actions/workflows/tests.yml/badge.svg) 6 | [![Go report](https://goreportcard.com/badge/github.com/wangliliwang/toolbox)](https://goreportcard.com/report/github.com/wangliliwang/toolbox) 7 | [![Coverage](https://img.shields.io/codecov/c/github/wangliliwang/toolbox)](https://codecov.io/gh/wangliliwang/toolbox) 8 | [![Contributors](https://img.shields.io/github/contributors/wangliliwang/toolbox)](https://github.com/wangliliwang/toolbox/graphs/contributors) 9 | [![License](https://img.shields.io/github/license/wangliliwang/toolbox)](./LICENSE) 10 | 11 | ✨ **`wangliliwang/toolbox` is a Lodash-style Go library based on Go 1.18+ Generics and is inspired by [samber/lo](https://github.com/samber/lo) and [lodash](https://lodash.com/).** 12 | 13 | ## 🚀 Install 14 | 15 | ```sh 16 | go get github.com/wangliliwang/toolbox 17 | ``` 18 | 19 | ## 💡 Usage 20 | 21 | You can import `toolbox` using: 22 | 23 | ```go 24 | import ( 25 | "github.com/wangliliwang/toolbox" 26 | ) 27 | ``` 28 | 29 | Then use one of the helpers below: 30 | 31 | ```go 32 | ch := toolbox.SliceToChannel[string]([]string{"Samuel", "John", "Samuel"}) 33 | ``` -------------------------------------------------------------------------------- /channel.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "math/rand" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type DispatchingStrategy[T any] func(msg T, index uint64, channels []<-chan T) int 10 | 11 | // ChannelDispatcher distributes messages from input channels into N child channels. 12 | // Close events are propagated to children. 13 | // Underlying channels can have a fixed buffer capacity or be unbuffered when cap is 0. 14 | func ChannelDispatcher[T any](stream <-chan T, count int, channelBufferCap int, strategy DispatchingStrategy[T]) []<-chan T { 15 | channels := createChannels[T](count, channelBufferCap) 16 | roChildren := channelsToReadonly(channels) 17 | go func() { 18 | defer closeChannels(channels) 19 | var index uint64 20 | for { 21 | msg, ok := <-stream 22 | if !ok { 23 | break 24 | } 25 | destination := strategy(msg, index, roChildren) % count 26 | channels[destination] <- msg 27 | index++ 28 | } 29 | }() 30 | return roChildren 31 | } 32 | 33 | func createChannels[T any](count, channelBufferCap int) []chan T { 34 | result := make([]chan T, 0, count) 35 | for i := 0; i < count; i++ { 36 | result = append(result, make(chan T, channelBufferCap)) 37 | } 38 | return result 39 | } 40 | 41 | func channelsToReadonly[T any](channels []chan T) []<-chan T { 42 | result := make([]<-chan T, 0) 43 | for _, c := range channels { 44 | result = append(result, c) 45 | } 46 | return result 47 | } 48 | 49 | func closeChannels[T any](channels []chan T) { 50 | for _, c := range channels { 51 | close(c) 52 | } 53 | } 54 | 55 | func channelIsNotFull[T any](ch <-chan T) bool { 56 | return cap(ch) == 0 || len(ch) < cap(ch) 57 | } 58 | 59 | // DispatchingStrategyRoundRobin distributes messages in a rotating sequential manner. 60 | // If the channel capacity is exceeded, the next channel will be selected and so on. 61 | func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan T) int { 62 | l := len(channels) 63 | for { 64 | result := int(index % uint64(l)) 65 | if channelIsNotFull(channels[result]) { 66 | return result 67 | } 68 | time.Sleep(10 * time.Millisecond) // prevent CPU from burning 🔥 69 | index++ 70 | } 71 | } 72 | 73 | // DispatchingStrategyRandom distributes messages in a random manner. 74 | // If the channel capacity is exceeded, another random channel will be selected and so on. 75 | func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T) int { 76 | l := len(channels) 77 | for { 78 | result := rand.Intn(l) 79 | if channelIsNotFull(channels[result]) { 80 | return result 81 | } 82 | time.Sleep(10 * time.Millisecond) // prevent CPU from burning 🔥 83 | } 84 | } 85 | 86 | // DispatchingStrategyWeightedRandom distributes messages in a weighted manner. 87 | // If the channel capacity is exceeded, another random channel will be selected and so on. 88 | func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy[T] { 89 | // todo(wangli) optimize here. 90 | seq := make([]int, 0) 91 | for i := 0; i < len(weights); i++ { 92 | for j := 0; j < weights[i]; j++ { 93 | seq = append(seq, i) 94 | } 95 | } 96 | 97 | return func(msg T, index uint64, channels []<-chan T) int { 98 | l := len(seq) 99 | for { 100 | result := seq[rand.Intn(l)] 101 | if channelIsNotFull(channels[result]) { 102 | return result 103 | } 104 | time.Sleep(10 * time.Millisecond) // prevent CPU from burning 🔥 105 | } 106 | } 107 | } 108 | 109 | // DispatchingStrategyFirst distributes messages in a first-non-full manner. 110 | // If the channel capacity is exceeded, the second channel will be selected and so on. 111 | func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) int { 112 | for { 113 | for i, ch := range channels { 114 | if channelIsNotFull(ch) { 115 | return i 116 | } 117 | } 118 | time.Sleep(10 * time.Millisecond) 119 | } 120 | } 121 | 122 | // DispatchingStrategyLeast distributes messages in the emptiest channel. 123 | func DispatchingStrategyLeast[T any](msg T, index uint64, channels []<-chan T) int { 124 | seq := Range[int](len(channels)) 125 | return FindBy(seq, func(a, b int) bool { 126 | return len(channels[a]) < len(channels[b]) 127 | }) 128 | } 129 | 130 | // DispatchingStrategyMost distributes messages in the fullest channel. 131 | // If the channel capacity is exceeded, the next channel will be selected and so on. 132 | func DispatchingStrategyMost[T any](msg T, index uint64, channels []<-chan T) int { 133 | seq := Range[int](len(channels)) 134 | return FindBy(seq, func(a, b int) bool { 135 | return (len(channels[a]) > len(channels[b])) && channelIsNotFull(channels[a]) 136 | }) 137 | } 138 | 139 | // SliceToChannel returns a read-only channel of collection items. 140 | func SliceToChannel[T any](collection []T, bufferSize int) <-chan T { 141 | ch := make(chan T, bufferSize) 142 | go func() { 143 | for _, item := range collection { 144 | ch <- item 145 | } 146 | close(ch) 147 | }() 148 | return ch 149 | } 150 | 151 | // ChannelToSlice returns a slice built from channel items. 152 | // Blocks until channel closes. 153 | func ChannelToSlice[T any](ch <-chan T) []T { 154 | collection := make([]T, 0) 155 | for item := range ch { 156 | collection = append(collection, item) 157 | } 158 | return collection 159 | } 160 | 161 | // Generator implements the generator design pattern. 162 | // Refer: https://github.com/tmrts/go-patterns/blob/master/concurrency/generator.md 163 | func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T { 164 | ch := make(chan T, bufferSize) 165 | go func() { 166 | // WARNING: infinite loop 167 | generator(func(t T) { 168 | ch <- t 169 | }) 170 | close(ch) 171 | }() 172 | return ch 173 | } 174 | 175 | // Buffer creates a slice of n elements from a channel. Returns the slice and slice length. 176 | func Buffer[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, closed bool) { 177 | collection = make([]T, 0, size) 178 | now := time.Now() 179 | closed = true 180 | for index := 0; index < size; index++ { 181 | value, ok := <-ch 182 | if !ok { 183 | closed = false 184 | break 185 | } 186 | collection = append(collection, value) 187 | } 188 | length = len(collection) 189 | readTime = time.Since(now) 190 | return 191 | } 192 | 193 | // BufferWithTimeout 194 | func BufferWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, closed bool) { 195 | expire := time.NewTimer(timeout) 196 | defer expire.Stop() 197 | 198 | collection = make([]T, 0, size) 199 | now := time.Now() 200 | closed = true 201 | for index := 0; index < size; index++ { 202 | select { 203 | case value, ok := <-ch: 204 | if !ok { 205 | closed = false 206 | break 207 | } 208 | collection = append(collection, value) 209 | case <-expire.C: 210 | break 211 | } 212 | } 213 | length = len(collection) 214 | readTime = time.Since(now) 215 | return 216 | } 217 | 218 | // FanIn collects items from multiple channels into a single buffered channel. 219 | // Output messages has no priority. When all upstream channels reach EOF, downstream channel closes. 220 | func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T { 221 | out := make(chan T, channelBufferCap) 222 | wg := sync.WaitGroup{} 223 | wg.Add(len(upstreams)) 224 | for _, upstream := range upstreams { 225 | go func(ch <-chan T) { 226 | for item := range ch { 227 | out <- item 228 | } 229 | wg.Done() 230 | }(upstream) 231 | } 232 | 233 | go func() { 234 | wg.Wait() 235 | close(out) // TODO(@wangli) reader has problems? 236 | }() 237 | 238 | return out 239 | } 240 | 241 | // FanOut broadcasts all upstream messages to multiple downstream channels. 242 | // When upstream channel EOF, downstream channels close. 243 | // If any downstream channel is full, broadcasting is paused. 244 | func FanOut[T any](count, channelBufferCap int, upstream <-chan T) []<-chan T { 245 | downstreamChannels := createChannels[T](count, channelBufferCap) 246 | 247 | go func() { 248 | for item := range upstream { 249 | for _, ch := range downstreamChannels { 250 | ch <- item 251 | } 252 | } 253 | closeChannels(downstreamChannels) 254 | }() 255 | 256 | return channelsToReadonly(downstreamChannels) 257 | } 258 | -------------------------------------------------------------------------------- /channel_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestChannelDispatcher(t *testing.T) { 10 | t.Parallel() 11 | is := assert.New(t) 12 | 13 | stream := make(chan int) 14 | go func() { 15 | for i := 0; i < 4; i++ { 16 | stream <- i 17 | } 18 | close(stream) 19 | }() 20 | channels := ChannelDispatcher(stream, 3, 0, DispatchingStrategyRoundRobin[int]) 21 | result0 := <-channels[0] 22 | is.Equal(0, result0) 23 | result1 := <-channels[1] 24 | is.Equal(1, result1) 25 | result2 := <-channels[2] 26 | is.Equal(2, result2) 27 | result3 := <-channels[0] 28 | is.Equal(3, result3) 29 | } 30 | 31 | func TestDispatchingStrategyRoundRobin(t *testing.T) { 32 | t.Parallel() 33 | is := assert.New(t) 34 | 35 | channels := make([]<-chan int, 5) 36 | for i := range channels { 37 | channels[i] = make(chan int, 1) 38 | } 39 | for i := 0; i < 100; i++ { 40 | result := DispatchingStrategyRoundRobin(0, uint64(i), channels) 41 | is.Equal(i%len(channels), result) 42 | } 43 | } 44 | 45 | func TestDispatchingStrategyRandom(t *testing.T) { 46 | t.Parallel() 47 | is := assert.New(t) 48 | 49 | channels := make([]<-chan int, 5) 50 | for i := range channels { 51 | channels[i] = make(chan int, 1) 52 | } 53 | for i := 0; i < 100; i++ { 54 | result := DispatchingStrategyRandom(0, uint64(i), channels) 55 | is.True(result < len(channels)) 56 | } 57 | } 58 | 59 | func TestDispatchingStrategyWeightedRandom(t *testing.T) { 60 | t.Parallel() 61 | is := assert.New(t) 62 | 63 | channels := make([]<-chan int, 5) 64 | for i := range channels { 65 | channels[i] = make(chan int, 1) 66 | } 67 | weights := []int{1, 2, 3, 2, 4} 68 | strategy := DispatchingStrategyWeightedRandom[int](weights) 69 | for i := 0; i < 100; i++ { 70 | result := strategy(0, uint64(i), channels) 71 | is.True(result < len(channels)) 72 | } 73 | } 74 | 75 | func TestDispatchingStrategyFirst(t *testing.T) { 76 | t.Parallel() 77 | is := assert.New(t) 78 | 79 | channels := make([]chan int, 5) 80 | for i := range channels { 81 | channels[i] = make(chan int, 1) 82 | } 83 | channels[0] <- 0 84 | for i := 0; i < 100; i++ { 85 | result := DispatchingStrategyFirst(0, uint64(i), channelsToReadonly(channels)) 86 | is.Equal(1, result) 87 | } 88 | } 89 | 90 | func TestDispatchingStrategyLeast(t *testing.T) { 91 | t.Parallel() 92 | is := assert.New(t) 93 | 94 | channels := make([]chan int, 5) 95 | for i := range channels { 96 | channels[i] = make(chan int, i) 97 | for j := 0; j < i; j++ { 98 | channels[i] <- j 99 | } 100 | } 101 | for i := 0; i < 100; i++ { 102 | result := DispatchingStrategyLeast(0, uint64(i), channelsToReadonly(channels)) 103 | is.Equal(0, result) 104 | } 105 | } 106 | 107 | func TestDispatchingStrategyMost(t *testing.T) { 108 | t.Parallel() 109 | is := assert.New(t) 110 | 111 | channels := make([]chan int, 5) 112 | for i := range channels { 113 | channels[i] = make(chan int, 10) 114 | for j := 0; j < i; j++ { 115 | channels[i] <- j 116 | } 117 | } 118 | for i := 0; i < 100; i++ { 119 | result := DispatchingStrategyMost(0, uint64(i), channelsToReadonly(channels)) 120 | is.Equal(4, result) 121 | } 122 | } 123 | 124 | func TestGenerator(t *testing.T) { 125 | t.Parallel() 126 | is := assert.New(t) 127 | 128 | is.Eventually(func() bool { 129 | ch := Generator[int](3, func(yield func(t int)) { 130 | for i := 0; i < 3; i++ { 131 | yield(i) 132 | } 133 | }) 134 | index := 0 135 | for item := range ch { 136 | is.Equal(index, item) 137 | index++ 138 | } 139 | return true 140 | }, time.Second, 10*time.Millisecond) 141 | } 142 | 143 | func TestBuffer(t *testing.T) { 144 | t.Parallel() 145 | is := assert.New(t) 146 | 147 | is.Eventually(func() bool { 148 | ch := make(chan int) 149 | go func() { 150 | for i := 0; i < 8; i++ { 151 | ch <- i 152 | } 153 | close(ch) 154 | }() 155 | 156 | // not close 157 | collection1, length1, _, ok1 := Buffer(ch, 4) 158 | is.Equal([]int{0, 1, 2, 3}, collection1) 159 | is.Equal(4, length1) 160 | is.True(ok1) 161 | 162 | // closed 163 | collection2, length2, _, ok2 := Buffer(ch, 5) 164 | is.Equal([]int{4, 5, 6, 7}, collection2) 165 | is.Equal(4, length2) 166 | is.False(ok2) 167 | 168 | return true 169 | }, time.Second, 10*time.Millisecond) 170 | } 171 | 172 | func TestBufferWithTimeout(t *testing.T) { 173 | t.Parallel() 174 | is := assert.New(t) 175 | 176 | is.Eventually(func() bool { 177 | ch := make(chan int) 178 | go func() { 179 | for i := 0; i < 9; i++ { 180 | if i != 0 && i%4 == 0 { 181 | time.Sleep(100 * time.Millisecond) 182 | } 183 | ch <- i 184 | } 185 | close(ch) 186 | }() 187 | 188 | // not close, not timeout 189 | collection1, length1, _, ok1 := BufferWithTimeout(ch, 4, 10*time.Millisecond) 190 | is.Equal([]int{0, 1, 2, 3}, collection1) 191 | is.Equal(4, length1) 192 | is.True(ok1) 193 | time.Sleep(100 * time.Millisecond) 194 | 195 | // not close, timeout 196 | collection2, length2, _, ok2 := BufferWithTimeout(ch, 5, 10*time.Millisecond) 197 | is.Equal([]int{4, 5, 6, 7}, collection2) 198 | is.Equal(4, length2) 199 | is.True(ok2) 200 | time.Sleep(100 * time.Millisecond) 201 | 202 | // close 203 | collection3, length3, _, ok3 := BufferWithTimeout(ch, 5, 10*time.Millisecond) 204 | is.Equal([]int{8}, collection3) 205 | is.Equal(1, length3) 206 | is.False(ok3) 207 | 208 | return true 209 | }, time.Second, 10*time.Millisecond) 210 | } 211 | 212 | func TestFanIn(t *testing.T) { 213 | t.Parallel() 214 | is := assert.New(t) 215 | 216 | is.Eventually(func() bool { 217 | channels := createChannels[int](3, 0) 218 | for index, item := range channels { 219 | go func(value int, ch chan int) { 220 | ch <- value 221 | close(ch) 222 | }(index, item) 223 | } 224 | out := FanIn(0, channelsToReadonly(channels)...) 225 | collection := ChannelToSlice(out) 226 | is.Equal(3, len(collection)) 227 | return true 228 | }, time.Second, 10*time.Millisecond) 229 | } 230 | 231 | func TestFanOut(t *testing.T) { 232 | t.Parallel() 233 | is := assert.New(t) 234 | is.Eventually(func() bool { 235 | upstream := make(chan int, 5) 236 | upstream <- 1 237 | upstream <- 2 238 | upstream <- 3 239 | close(upstream) 240 | downstreams := FanOut(3, 5, upstream) 241 | for _, downstream := range downstreams { 242 | index := 1 243 | for item := range downstream { 244 | is.Equal(index, item) 245 | index++ 246 | } 247 | } 248 | return true 249 | }, time.Second, 10*time.Millisecond) 250 | } 251 | 252 | func TestSliceToChannel(t *testing.T) { 253 | t.Parallel() 254 | is := assert.New(t) 255 | is.Eventually(func() bool { 256 | collection := []int{1, 2, 3, 4, 5} 257 | ch := SliceToChannel(collection, 0) 258 | outCollection := ChannelToSlice(ch) 259 | is.Equal(collection, outCollection) 260 | return true 261 | }, time.Second, 10*time.Millisecond) 262 | } 263 | -------------------------------------------------------------------------------- /cmd/generate_tuples.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/wangliliwang/toolbox" 7 | "os" 8 | "strings" 9 | "text/template" 10 | ) 11 | 12 | var ( 13 | tupleStartIndex = 2 14 | tupleEndIndex = 17 15 | tuplesTmplString = `// Code generated by go run cmd/generate_tuples.go -output tuples.go 16 | // DO NOT EDIT 17 | 18 | package toolbox 19 | 20 | {{ range $index, $item := . }} 21 | type {{ $item.TupleStructName }}[{{ $item.GenericDeclaration }}] struct { 22 | {{ $item.TupleStructBody }} 23 | } 24 | 25 | // {{ $item.PackFuncName }} returns a {{ $item.TupleStructName }} instance. 26 | func {{ $item.PackFuncName }}[{{ $item.GenericDeclaration }}]({{ $item.PackFuncParam }}) {{ $item.TupleStructName }}[{{ $item.GenericParam }}] { 27 | return {{ $item.TupleStructName }}[{{ $item.GenericParam }}]{ {{ $item.TupleInstanceBody }} } 28 | } 29 | 30 | // {{ $item.UnpackFuncName }} returns a {{ $item.TupleStructName }}'s inner value. 31 | func {{ $item.UnpackFuncName }}[{{ $item.GenericDeclaration }}]({{ $item.UnpackFuncParam }}) ({{ $item.GenericParam }}) { 32 | return {{ $item.UnpackFuncReturnValue }} 33 | } 34 | 35 | // {{ $item.ZipFuncName }} returns a {{ $item.TupleStructName }} slice, whose length is max of input collections. 36 | func {{ $item.ZipFuncName }}[{{ $item.GenericDeclaration }}]({{ $item.ZipFuncParam }}) []{{ $item.TupleStructName }}[{{ $item.GenericParam }}] { 37 | maxLength := Max({{ $item.ZipFuncMaxFuncParam }}) 38 | result := make([]{{ $item.TupleStructName }}[{{ $item.GenericParam }}], maxLength) 39 | for index := 0; index < maxLength; index++ { 40 | {{ $item.ZipFuncBodyNth }} 41 | result[index] = {{ $item.TupleStructName }}[{{ $item.GenericParam }}]{ {{ $item.TupleInstanceBody }} } 42 | } 43 | return result 44 | } 45 | 46 | // {{ $item.UnzipFuncName }} returns {{ $item.Index }} slices, whose elements come from {{ $item.TupleStructName }}-collection. 47 | func {{ $item.UnzipFuncName }}[{{ $item.GenericDeclaration }}](collection []{{ $item.TupleStructName }}[{{ $item.GenericParam }}]) ({{ $item.UnzipReturnParam }}) { 48 | length := len(collection) 49 | {{ $item.UnzipResultSlice }} 50 | for index := 0; index < length; index++ { 51 | {{ $item.UnzipLoopBody }} 52 | } 53 | return {{ $item.UnzipReturnValue }} 54 | } 55 | {{ end }} 56 | ` 57 | ) 58 | 59 | type Item struct { 60 | Index int // 2 61 | GenericDeclaration string // T1 any, T2 any 62 | GenericParam string // T1, T2 63 | 64 | TupleStructName string // Tuple2 65 | TupleStructBody string // A T1\n B T2 66 | TupleInstanceBody string // A: a, B: b 67 | 68 | PackFuncName string // Pack2 69 | PackFuncParam string // a T1, b T2 70 | 71 | UnpackFuncName string // Unpack2 72 | UnpackFuncParam string // t2 Tuple2[T1, T2] 73 | UnpackFuncReturnValue string // t2.A, t2.B 74 | 75 | ZipFuncName string // Zip2 76 | ZipFuncParam string // collection1 []T1, collection2 []T2 77 | ZipFuncMaxFuncParam string // len(collection1), len(collection2) 78 | ZipFuncBodyNth string // a, _ := Nth(collection1, i)\n b, _ := Nth(collection2, i) 79 | 80 | UnzipFuncName string // Unzip2 81 | UnzipResultSlice string // result1 := make([]T1, len(collection))\n result2 := make([]T2, len(collection)) 82 | UnzipReturnParam string // []T1, []T2 83 | UnzipLoopBody string // result1[i] = collection[i].A\n result2[i] = collection[i].B 84 | UnzipReturnValue string // result1, result2 85 | } 86 | 87 | func main() { 88 | // parse input 89 | output := flag.String("output", "tuples.go", "file name to write") 90 | if output == nil { 91 | panic("nil outputFilename") 92 | } 93 | outputFilename := strings.TrimSpace(*output) 94 | if outputFilename == "" { 95 | panic("empty outputFilename") 96 | } 97 | 98 | // generate 99 | lowerLetters := toolbox.Map(toolbox.RangeFrom('a', 26), func(item int32, index int) string { 100 | return string(byte(item)) 101 | }) 102 | upperLetters := toolbox.Map(toolbox.RangeFrom('A', 26), func(item int32, index int) string { 103 | return string(byte(item)) 104 | }) 105 | items := make([]Item, 0) 106 | for i := tupleStartIndex; i <= tupleEndIndex; i++ { 107 | genericDeclarations := make([]string, 0) 108 | genericParams := make([]string, 0) 109 | tupleStructBodies := make([]string, 0) 110 | tupleInstanceBodies := make([]string, 0) 111 | packFuncParams := make([]string, 0) 112 | unpackFuncReturnValues := make([]string, 0) 113 | zipFuncParams := make([]string, 0) 114 | zipFuncMaxFuncParams := make([]string, 0) 115 | zipFuncBodyNths := make([]string, 0) 116 | unzipResultSlices := make([]string, 0) 117 | unzipReturnParams := make([]string, 0) 118 | unzipLoopBodies := make([]string, 0) 119 | unzipReturnValues := make([]string, 0) 120 | for j := 1; j <= i; j++ { 121 | genericDeclarations = append(genericDeclarations, fmt.Sprintf("T%d any", j)) 122 | genericParams = append(genericParams, fmt.Sprintf("T%d", j)) 123 | tupleStructBodies = append(tupleStructBodies, fmt.Sprintf("%s T%d", upperLetters[j-1], j)) 124 | tupleInstanceBodies = append(tupleInstanceBodies, fmt.Sprintf("%s: %s", upperLetters[j-1], lowerLetters[j-1])) 125 | packFuncParams = append(packFuncParams, fmt.Sprintf("%s T%d", lowerLetters[j-1], j)) 126 | unpackFuncReturnValues = append(unpackFuncReturnValues, fmt.Sprintf("t%d.%s", i, upperLetters[j-1])) 127 | zipFuncParams = append(zipFuncParams, fmt.Sprintf("collection%d []T%d", j, j)) 128 | zipFuncMaxFuncParams = append(zipFuncMaxFuncParams, fmt.Sprintf("len(collection%d)", j)) 129 | zipFuncBodyNths = append(zipFuncBodyNths, fmt.Sprintf("%s, _ := Nth(collection%d, index)", lowerLetters[j-1], j)) 130 | unzipResultSlices = append(unzipResultSlices, fmt.Sprintf("result%d := make([]T%d, length)", j, j)) 131 | unzipReturnParams = append(unzipReturnParams, fmt.Sprintf("[]T%d", j)) 132 | unzipLoopBodies = append(unzipLoopBodies, fmt.Sprintf("result%d[index] = collection[index].%s", j, upperLetters[j-1])) 133 | unzipReturnValues = append(unzipReturnValues, fmt.Sprintf("result%d", j)) 134 | } 135 | 136 | items = append(items, Item{ 137 | Index: i, 138 | GenericDeclaration: strings.Join(genericDeclarations, ", "), // T1 any, T2 any 139 | GenericParam: strings.Join(genericParams, ", "), // T1, T2 140 | 141 | TupleStructName: fmt.Sprintf("Tuple%d", i), // Tuple2 142 | TupleStructBody: strings.Join(tupleStructBodies, "\n\t"), // A T1\n B T2 143 | TupleInstanceBody: strings.Join(tupleInstanceBodies, ", "), // A: a, B: b 144 | 145 | PackFuncName: fmt.Sprintf("Pack%d", i), // Pack2 146 | PackFuncParam: strings.Join(packFuncParams, ", "), // a T1, b T2 147 | 148 | UnpackFuncName: fmt.Sprintf("Unpack%d", i), // Unpack2 149 | UnpackFuncParam: fmt.Sprintf("t%d Tuple%d[%s]", i, i, strings.Join(genericParams, ", ")), // t2 Tuple2[T1, T2] 150 | UnpackFuncReturnValue: strings.Join(unpackFuncReturnValues, ", "), // t2.A, t2.B 151 | 152 | ZipFuncName: fmt.Sprintf("Zip%d", i), // Zip2 153 | ZipFuncParam: strings.Join(zipFuncParams, ", "), // collection1 []T1, collection2 []T2 154 | ZipFuncMaxFuncParam: strings.Join(zipFuncMaxFuncParams, ", "), // len(collection1), len(collection2) 155 | ZipFuncBodyNth: strings.Join(zipFuncBodyNths, "\n\t\t"), // a, _ := Nth(collection1, i)\n b, _ := Nth(collection2, i) 156 | 157 | UnzipFuncName: fmt.Sprintf("Unzip%d", i), // Unzip2 158 | UnzipResultSlice: strings.Join(unzipResultSlices, "\n\t"), // result1 := make([]T1, len(collection))\n result2 := make([]T2, len(collection)) 159 | UnzipReturnParam: strings.Join(unzipReturnParams, ", "), // []T1, []T2 160 | UnzipLoopBody: strings.Join(unzipLoopBodies, "\n\t\t"), // result1[i] = collection[i].A\n result2[i] = collection[i].B 161 | UnzipReturnValue: strings.Join(unzipReturnValues, ", "), // result1, result2 162 | }) 163 | } 164 | tmpl, parseErr := template.New("tuples").Parse(tuplesTmplString) 165 | toolbox.PanicIf(parseErr) 166 | 167 | f, openErr := os.OpenFile(outputFilename, os.O_CREATE|os.O_WRONLY, 0644) 168 | toolbox.PanicIf(openErr) 169 | 170 | executeErr := tmpl.Execute(f, items) 171 | toolbox.PanicIf(executeErr) 172 | } 173 | -------------------------------------------------------------------------------- /condition.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | // Ternary is a one line if-else statement 4 | func Ternary[T any](condition bool, ifOutput, elseOutput T) T { 5 | if condition { 6 | return ifOutput 7 | } 8 | return elseOutput 9 | } 10 | 11 | // TernaryF is a one line if-else statement whose options are functions. 12 | func TernaryF[T any](condition bool, ifFunc, elseFunc func() T) T { 13 | if condition { 14 | return ifFunc() 15 | } 16 | return elseFunc() 17 | } 18 | 19 | type ifElse[T any] struct { 20 | done bool 21 | result T 22 | } 23 | 24 | func (i *ifElse[T]) Else(t T) T { 25 | if !i.done { 26 | return t 27 | } 28 | return i.result 29 | } 30 | 31 | func (i *ifElse[T]) ElseF(predicate func() T) T { 32 | if !i.done { 33 | return predicate() 34 | } 35 | return i.result 36 | } 37 | 38 | func (i *ifElse[T]) ElseIf(condition bool, t T) *ifElse[T] { 39 | if !i.done { 40 | if condition { 41 | return &ifElse[T]{ 42 | done: true, 43 | result: t, 44 | } 45 | } 46 | var zero T 47 | return &ifElse[T]{ 48 | done: false, 49 | result: zero, 50 | } 51 | } 52 | return i 53 | } 54 | 55 | func (i *ifElse[T]) ElseIfF(condition bool, predicate func() T) *ifElse[T] { 56 | if !i.done { 57 | if condition { 58 | return &ifElse[T]{ 59 | done: true, 60 | result: predicate(), 61 | } 62 | } 63 | var zero T 64 | return &ifElse[T]{ 65 | done: false, 66 | result: zero, 67 | } 68 | } 69 | return i 70 | } 71 | 72 | func If[T any](condition bool, t T) *ifElse[T] { 73 | if condition { 74 | return &ifElse[T]{ 75 | done: true, 76 | result: t, 77 | } 78 | } 79 | var zero T 80 | return &ifElse[T]{ 81 | done: false, 82 | result: zero, 83 | } 84 | } 85 | 86 | func IfF[T any](condition bool, predicate func() T) *ifElse[T] { 87 | if condition { 88 | return &ifElse[T]{ 89 | done: true, 90 | result: predicate(), 91 | } 92 | } 93 | var zero T 94 | return &ifElse[T]{ 95 | done: false, 96 | result: zero, 97 | } 98 | } 99 | 100 | type switchCase[T any] struct { 101 | predicate T 102 | done bool 103 | result T 104 | } 105 | 106 | func (s *switchCase[T]) Case(condition bool, t T) *switchCase[T] { 107 | if s.done { 108 | return s 109 | } 110 | if condition { 111 | return &switchCase[T]{ 112 | predicate: s.predicate, 113 | done: true, 114 | result: t, 115 | } 116 | } 117 | var zero T 118 | return &switchCase[T]{ 119 | predicate: s.predicate, 120 | done: false, 121 | result: zero, 122 | } 123 | } 124 | 125 | func (s *switchCase[T]) CaseF(condition bool, t func(predicate T) T) *switchCase[T] { 126 | if s.done { 127 | return s 128 | } 129 | if condition { 130 | return &switchCase[T]{ 131 | predicate: s.predicate, 132 | done: true, 133 | result: t(s.predicate), 134 | } 135 | } 136 | var zero T 137 | return &switchCase[T]{ 138 | predicate: s.predicate, 139 | done: false, 140 | result: zero, 141 | } 142 | } 143 | 144 | func (s *switchCase[T]) Default(t T) T { 145 | if s.done { 146 | return s.result 147 | } 148 | return t 149 | } 150 | 151 | func (s *switchCase[T]) DefaultF(t func(predicate T) T) T { 152 | if s.done { 153 | return s.result 154 | } 155 | return t(s.predicate) 156 | } 157 | 158 | func Switch[T any](predicate T) *switchCase[T] { 159 | var zero T 160 | return &switchCase[T]{ 161 | predicate: predicate, 162 | done: false, 163 | result: zero, 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /condition_example_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import "fmt" 4 | 5 | func ExampleTernary() { 6 | result1 := Ternary(true, 1, 2) 7 | result2 := Ternary(false, 1, 2) 8 | fmt.Printf("%+v %+v", result1, result2) 9 | // Output: 10 | // 1 2 11 | } 12 | 13 | func ExampleTernaryF() { 14 | result1 := TernaryF(true, func() int { 15 | return 1 16 | }, func() int { 17 | return 2 18 | }) 19 | result2 := TernaryF(false, func() int { 20 | return 1 21 | }, func() int { 22 | return 2 23 | }) 24 | fmt.Printf("%+v %+v", result1, result2) 25 | // Output: 26 | // 1 2 27 | } 28 | 29 | func ExampleIf() { 30 | result1 := If(true, 1). 31 | Else(2) 32 | result2 := If(false, 1). 33 | Else(2) 34 | fmt.Printf("%+v %+v", result1, result2) 35 | // Output: 36 | // 1 2 37 | } 38 | 39 | func ExampleIfF() { 40 | result1 := IfF(true, func() int { return 1 }). 41 | ElseF(func() int { return 2 }) 42 | result2 := IfF(false, func() int { return 1 }). 43 | ElseF(func() int { return 2 }) 44 | fmt.Printf("%+v %+v", result1, result2) 45 | // Output: 46 | // 1 2 47 | } 48 | 49 | func ExampleifElse_ElseIf() { 50 | result1 := If(false, 1). 51 | ElseIf(true, 2). 52 | Else(3) 53 | result2 := If(false, 1). 54 | ElseIf(false, 2). 55 | Else(3) 56 | fmt.Printf("%+v %+v", result1, result2) 57 | // Output: 58 | // 2 3 59 | } 60 | 61 | func ExampleifElse_ElseIfF() { 62 | result1 := If(false, 1). 63 | ElseIfF(true, func() int { return 2 }). 64 | Else(3) 65 | result2 := If(false, 1). 66 | ElseIfF(false, func() int { return 2 }). 67 | Else(3) 68 | fmt.Printf("%+v %+v", result1, result2) 69 | // Output: 70 | // 2 3 71 | } 72 | 73 | func ExampleSwitch() { 74 | result1 := Switch(1). 75 | Case(false, 3). 76 | Case(true, 4). 77 | Default(0) 78 | result2 := Switch(1). 79 | Case(false, 3). 80 | Case(false, 4). 81 | Default(0) 82 | result3 := Switch(1). 83 | CaseF(false, func(predicate int) int { return 3 }). 84 | CaseF(true, func(predicate int) int { return 4 }). 85 | DefaultF(func(predicate int) int { return 0 }) 86 | result4 := Switch(1). 87 | CaseF(false, func(predicate int) int { return 3 }). 88 | CaseF(false, func(predicate int) int { return 4 }). 89 | DefaultF(func(predicate int) int { return 0 }) 90 | fmt.Printf("%+v %+v %+v %v", result1, result2, result3, result4) 91 | // Output: 92 | // 4 0 4 0 93 | } 94 | -------------------------------------------------------------------------------- /constraints.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | type Cloneable[T any] interface { 4 | Clone() T 5 | } 6 | -------------------------------------------------------------------------------- /error_example.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | func PanicIf(err error) { 4 | if err != nil { 5 | panic(err) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /error_example_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | func wrapPanicIf(srcErr error) (err error) { 9 | defer func() { 10 | if r := recover(); r != nil { 11 | if e, ok := r.(error); ok { 12 | err = fmt.Errorf("%w", e) 13 | } else { 14 | err = fmt.Errorf("unknown panic") 15 | } 16 | } 17 | }() 18 | PanicIf(srcErr) 19 | return nil 20 | } 21 | 22 | func ExamplePanicIf() { 23 | var err error 24 | fmt.Println(wrapPanicIf(err)) 25 | err = errors.New("example err") 26 | fmt.Println(wrapPanicIf(err)) 27 | // Output: 28 | // 29 | // example err 30 | } 31 | -------------------------------------------------------------------------------- /find.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | // FindBy returns minimum element in collection by comparison function. 4 | // If there are multi minimum values, MinBy returns the first. 5 | // If collection is empty, MinBy returns zero value of T. 6 | // todo(wangli) Change name and description. 7 | func FindBy[T any](collection []T, comparison func(a, b T) bool) T { 8 | var min T 9 | if len(collection) == 0 { 10 | return min 11 | } 12 | min = collection[0] 13 | for i := 1; i < len(collection); i++ { 14 | if comparison(collection[i], min) { 15 | min = collection[i] 16 | } 17 | } 18 | return min 19 | } 20 | -------------------------------------------------------------------------------- /find_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestFindBy(t *testing.T) { 9 | t.Parallel() 10 | is := assert.New(t) 11 | 12 | min := FindBy([]int{1, 2, 3, 4, 5}, func(a, b int) bool { 13 | return a < b 14 | }) 15 | is.Equal(1, min) 16 | 17 | max := FindBy([]int{1, 2, 3, 4, 5}, func(a, b int) bool { 18 | return a > b 19 | }) 20 | is.Equal(5, max) 21 | 22 | zero := FindBy([]int{}, func(a, b int) bool { 23 | return a > b 24 | }) 25 | is.Equal(0, zero) 26 | } 27 | -------------------------------------------------------------------------------- /func.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "fmt" 5 | "runtime/debug" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // SafelyRun try to catch panic during f-runtime, and transform it into error. 11 | func SafelyRun(function func()) (err error) { 12 | defer func() { 13 | if r := recover(); r != nil { 14 | if e, ok := r.(error); ok { 15 | err = fmt.Errorf("%w\n%s", e, debug.Stack()) 16 | } else { 17 | err = fmt.Errorf("unknown error\n%s", debug.Stack()) 18 | } 19 | } 20 | }() 21 | function() 22 | return nil 23 | } 24 | 25 | // After creates a function that invokes input `function` once it's called n or more times. 26 | func After(n int, function func()) func() { 27 | var count int 28 | return func() { 29 | count++ 30 | if count >= n { 31 | function() 32 | } 33 | } 34 | } 35 | 36 | type debounceState int 37 | 38 | const ( 39 | debounceStateNotTiming debounceState = 1 40 | debounceStateTiming debounceState = 2 41 | debounceStateCanceled debounceState = 3 42 | ) 43 | 44 | type debounceAction int 45 | 46 | const ( 47 | debounceActionCall debounceAction = 1 48 | debounceActionFlush debounceAction = 2 49 | debounceActionCancel debounceAction = 3 50 | ) 51 | 52 | // debounce implement debounce function. 53 | // FIXME(@wangli) There're some problems in change states. 54 | type debounce struct { 55 | // inner 56 | actionCh chan debounceAction 57 | timer *time.Timer 58 | mu sync.RWMutex 59 | state debounceState 60 | 61 | // outer 62 | f func() 63 | delay time.Duration 64 | } 65 | 66 | func newDebounce(function func(), delay time.Duration) *debounce { 67 | timer := time.NewTimer(delay) 68 | timer.Stop() 69 | dbc := &debounce{ 70 | actionCh: make(chan debounceAction), 71 | timer: timer, 72 | mu: sync.RWMutex{}, 73 | f: function, 74 | delay: delay, 75 | } 76 | // state machine 77 | go func() { 78 | defer dbc.closeResources() 79 | for { 80 | select { 81 | case v, ok := <-dbc.actionCh: 82 | if !ok { 83 | return 84 | } 85 | switch v { 86 | case debounceActionCall: 87 | dbc.onCall() 88 | case debounceActionFlush: 89 | dbc.onFlush() 90 | case debounceActionCancel: 91 | dbc.onCancel() 92 | return 93 | } 94 | case <-dbc.timer.C: 95 | dbc.onTimer() 96 | } 97 | } 98 | }() 99 | return dbc 100 | } 101 | 102 | func (d *debounce) closeResources() { 103 | close(d.actionCh) 104 | d.timer.Stop() 105 | } 106 | 107 | func (d *debounce) onCancel() { 108 | d.mu.Lock() 109 | defer d.mu.Unlock() 110 | 111 | if d.state == debounceStateCanceled { 112 | panic("can't cancel canceled debounce") 113 | } 114 | d.state = debounceStateCanceled 115 | d.timer.Stop() 116 | } 117 | 118 | func (d *debounce) onFlush() { 119 | d.mu.Lock() 120 | defer d.mu.Unlock() 121 | 122 | if d.state == debounceStateCanceled { 123 | panic("can't flush canceled debounce") 124 | } 125 | d.state = debounceStateNotTiming 126 | d.timer.Stop() 127 | d.f() 128 | } 129 | 130 | func (d *debounce) onCall() { 131 | d.mu.Lock() 132 | defer d.mu.Unlock() 133 | 134 | if d.state == debounceStateCanceled { 135 | panic("can't call canceled debounce") 136 | } 137 | d.state = debounceStateTiming 138 | d.timer.Reset(d.delay) 139 | } 140 | 141 | func (d *debounce) onTimer() { 142 | d.mu.Lock() 143 | defer d.mu.Unlock() 144 | 145 | if d.state == debounceStateCanceled || d.state == debounceStateNotTiming { 146 | panic("timer not stopped correctly") 147 | } 148 | 149 | d.state = debounceStateNotTiming 150 | d.timer.Stop() 151 | d.f() 152 | } 153 | 154 | func (d *debounce) cancel() { 155 | d.actionCh <- debounceActionCancel 156 | } 157 | 158 | func (d *debounce) flush() { 159 | d.actionCh <- debounceActionFlush 160 | } 161 | 162 | func (d *debounce) call() { 163 | d.actionCh <- debounceActionCall 164 | } 165 | 166 | // NewDebounce returns call, flush and cancel function in debounce. 167 | func NewDebounce(function func(), delay time.Duration) (call, flush, cancel func()) { 168 | dbc := newDebounce(function, delay) 169 | return dbc.call, dbc.flush, dbc.cancel 170 | } 171 | 172 | // Memoize creates a function that memoize the result of func. 173 | func Memoize[T any](function func(key string) T) func(key string) T { 174 | cache := make(map[string]T) 175 | return func(key string) T { 176 | if v, ok := cache[key]; ok { 177 | return v 178 | } else { 179 | calculatedV := function(key) 180 | cache[key] = calculatedV 181 | return calculatedV 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /func_example_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func ExampleSafelyRun() { 11 | err := SafelyRun(func() { 12 | panic(errors.New("example err")) 13 | }) 14 | fmt.Println(err) 15 | } 16 | 17 | func ExampleAfter() { 18 | result := After(3, func() { 19 | fmt.Println("info") 20 | }) 21 | result() 22 | result() 23 | result() 24 | result() 25 | // Output: 26 | // info 27 | // info 28 | } 29 | 30 | func ExampleNewDebounce() { 31 | call, flush, _ := NewDebounce(func() { 32 | fmt.Println("exec") 33 | }, 300*time.Millisecond) 34 | wg := sync.WaitGroup{} 35 | wg.Add(1) 36 | go func() { 37 | for i := 0; i < 3; i++ { 38 | time.Sleep(100 * time.Millisecond) 39 | call() 40 | } 41 | flush() 42 | for i := 0; i < 3; i++ { 43 | time.Sleep(100 * time.Millisecond) 44 | call() 45 | } 46 | wg.Done() 47 | }() 48 | wg.Wait() 49 | time.Sleep(500 * time.Millisecond) 50 | // Output: 51 | // exec 52 | // exec 53 | } 54 | -------------------------------------------------------------------------------- /func_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wangliliwang/toolbox 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/pmezard/go-difflib v1.0.0 // indirect 8 | github.com/spf13/cast v1.5.1 // indirect 9 | github.com/stretchr/objx v0.5.0 // indirect 10 | github.com/stretchr/testify v1.8.4 // indirect 11 | golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect 12 | golang.org/x/text v0.14.0 // indirect 13 | gopkg.in/yaml.v3 v3.0.1 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= 5 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 6 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 7 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 8 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 9 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 10 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 11 | github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= 12 | github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= 13 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 14 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 15 | github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= 16 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 17 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 18 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 19 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 20 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 21 | golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= 22 | golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= 23 | golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= 24 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 25 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 26 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 27 | golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= 28 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 29 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 30 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 31 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 32 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 33 | -------------------------------------------------------------------------------- /intersect.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | // Intersect returns intersection of list1 and list2 4 | func Intersect[T comparable](list1 []T, list2 []T) []T { 5 | result := make([]T, 0) 6 | seen := CollectionToSet(list1) 7 | for _, item := range list2 { 8 | if _, ok := seen[item]; ok { 9 | result = append(result, item) 10 | } 11 | } 12 | return result 13 | } 14 | 15 | // Subtract returns subtraction of list1 and list2, that is result of list1 - list2. 16 | func Subtract[T comparable](list1 []T, list2 []T) []T { 17 | result := make([]T, 0) 18 | seen2 := CollectionToSet(list2) 19 | for _, item := range list1 { 20 | if _, ok := seen2[item]; !ok { 21 | result = append(result, item) 22 | } 23 | } 24 | return result 25 | } 26 | 27 | // Union returns union of list. 28 | func Union[T comparable](lists ...[]T) []T { 29 | seen := make(map[T]struct{}) 30 | result := make([]T, 0) 31 | for _, list := range lists { 32 | for _, item := range list { 33 | if _, ok := seen[item]; !ok { 34 | seen[item] = struct{}{} 35 | result = append(result, item) 36 | } 37 | } 38 | } 39 | return result 40 | } 41 | -------------------------------------------------------------------------------- /intersect_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestIntersect(t *testing.T) { 10 | t.Parallel() 11 | is := assert.New(t) 12 | 13 | result1 := Intersect([]int{1, 2, 3, 4, 5}, []int{3, 4, 5, 6, 7}) 14 | result2 := Intersect([]int{1, 2, 3, 4, 5}, []int{6, 7, 8}) 15 | 16 | is.Equal([]int{3, 4, 5}, result1) 17 | is.Equal([]int{}, result2) 18 | } 19 | 20 | func TestSubtract(t *testing.T) { 21 | t.Parallel() 22 | is := assert.New(t) 23 | 24 | result1 := Subtract([]int{}, []int{1, 2, 3}) 25 | result2 := Subtract([]int{1}, []int{1, 2, 3}) 26 | result3 := Subtract([]int{1, 2, 3, 4}, []int{1, 2, 3}) 27 | 28 | is.Equal([]int{}, result1) 29 | is.Equal([]int{}, result2) 30 | is.Equal([]int{4}, result3) 31 | } 32 | 33 | func TestUnion(t *testing.T) { 34 | t.Parallel() 35 | is := assert.New(t) 36 | 37 | result1 := Union([]int{1, 1, 2, 3}, []int{1, 2, 3, 4}) 38 | result2 := Union([]int{1, 1, 2, 3}, []int{}) 39 | 40 | is.Equal([]int{1, 2, 3, 4}, result1) 41 | is.Equal([]int{1, 2, 3}, result2) 42 | } 43 | -------------------------------------------------------------------------------- /map.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | // OmitBy returns a map whose elements the predicate function returns falsey for, from in-map. 4 | func OmitBy[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) map[K]V { 5 | result := make(map[K]V) 6 | for k, v := range in { 7 | if !predicate(k, v) { 8 | result[k] = v 9 | } 10 | } 11 | return result 12 | } 13 | 14 | // OmitByKeys returns a map exclude keys in `keys` from in-map. 15 | func OmitByKeys[K comparable, V any](in map[K]V, keys []K) map[K]V { 16 | result := make(map[K]V) 17 | keysSet := CollectionToSet(keys) 18 | for k, v := range in { 19 | if _, ok := keysSet[k]; !ok { 20 | result[k] = v 21 | } 22 | } 23 | return result 24 | } 25 | 26 | // OmitByValues returns a map exclude values in `values` from in-map. 27 | func OmitByValues[K comparable, V comparable](in map[K]V, values []V) map[K]V { 28 | result := make(map[K]V) 29 | valuesSet := CollectionToSet(values) 30 | for k, v := range in { 31 | if _, ok := valuesSet[v]; !ok { 32 | result[k] = v 33 | } 34 | } 35 | return result 36 | } 37 | 38 | // PickBy returns a map whose elements the predicate function returns truthy for, from in-map. 39 | func PickBy[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) map[K]V { 40 | result := make(map[K]V) 41 | for k, v := range in { 42 | if predicate(k, v) { 43 | result[k] = v 44 | } 45 | } 46 | return result 47 | } 48 | 49 | // PickByKeys returns a map include keys in `keys` from in-map. 50 | func PickByKeys[K comparable, V any](in map[K]V, keys []K) map[K]V { 51 | result := make(map[K]V) 52 | keysSet := CollectionToSet(keys) 53 | for k, v := range in { 54 | if _, ok := keysSet[k]; ok { 55 | result[k] = v 56 | } 57 | } 58 | return result 59 | } 60 | 61 | // PickByValues returns a map include values in `values` from in-map. 62 | func PickByValues[K comparable, V comparable](in map[K]V, values []V) map[K]V { 63 | result := make(map[K]V) 64 | valuesSet := CollectionToSet(values) 65 | for k, v := range in { 66 | if _, ok := valuesSet[v]; ok { 67 | result[k] = v 68 | } 69 | } 70 | return result 71 | } 72 | 73 | type Entry[K comparable, V any] struct { 74 | Key K 75 | Value V 76 | } 77 | 78 | // ToEntries returns an entries of array in in-map. 79 | func ToEntries[K comparable, V any](in map[K]V) []Entry[K, V] { 80 | result := make([]Entry[K, V], 0, len(in)) 81 | for k, v := range in { 82 | result = append(result, Entry[K, V]{k, v}) 83 | } 84 | return result 85 | } 86 | 87 | // FromEntries returns a map build by entries. 88 | func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V { 89 | result := make(map[K]V) 90 | for _, entry := range entries { 91 | result[entry.Key] = entry.Value 92 | } 93 | return result 94 | } 95 | 96 | // Invert returns a map whose keys are values in in-map, and values are keys in in-map. 97 | func Invert[K comparable, V comparable](in map[K]V) map[V]K { 98 | result := make(map[V]K) 99 | for k, v := range in { 100 | result[v] = k 101 | } 102 | return result 103 | } 104 | 105 | // MapOverMap runs iteratee function in all key-values over in-map. 106 | func MapOverMap[K comparable, V any](in map[K]V, iteratee func(k K, v V)) { 107 | for k, v := range in { 108 | iteratee(k, v) 109 | } 110 | } 111 | 112 | // MapKeys returns a map whose keys is generated by iteratee function. 113 | func MapKeys[K comparable, V any](in map[K]V, iteratee func(k K, v V) K) map[K]V { 114 | result := make(map[K]V) 115 | for k, v := range in { 116 | result[iteratee(k, v)] = v 117 | } 118 | return result 119 | } 120 | 121 | // MapValues returns a map whose values is generated by iteratee function. 122 | func MapValues[K comparable, V any](in map[K]V, iteratee func(k K, v V) V) map[K]V { 123 | result := make(map[K]V) 124 | for k, v := range in { 125 | result[k] = iteratee(k, v) 126 | } 127 | return result 128 | } 129 | 130 | // MapEntries returns a map whose key-values is generated by iteratee function. 131 | func MapEntries[K comparable, V any](in map[K]V, iteratee func(k K, v V) (K, V)) map[K]V { 132 | result := make(map[K]V) 133 | for k, v := range in { 134 | newK, newV := iteratee(k, v) 135 | result[newK] = newV 136 | } 137 | return result 138 | } 139 | 140 | // Merge returns a map that contains all key-values on in-maps. 141 | func Merge[K comparable, V any](ins ...map[K]V) map[K]V { 142 | result := make(map[K]V) 143 | for _, in := range ins { 144 | for k, v := range in { 145 | result[k] = v 146 | } 147 | } 148 | return result 149 | } 150 | 151 | // Keys return an array of keys in map. 152 | func Keys[K comparable, V any](in map[K]V) []K { 153 | result := make([]K, 0, len(in)) 154 | for k := range in { 155 | result = append(result, k) 156 | } 157 | return result 158 | } 159 | 160 | // Values return an array of values in map. 161 | func Values[K comparable, V any](in map[K]V) []V { 162 | result := make([]V, 0, len(in)) 163 | for _, v := range in { 164 | result = append(result, v) 165 | } 166 | return result 167 | } 168 | 169 | // ValueOr return value for key in map. 170 | // If key not exists in map, return fallback value. 171 | func ValueOr[K comparable, V any](in map[K]V, key K, fallback V) V { 172 | if v, ok := in[key]; !ok { 173 | return fallback 174 | } else { 175 | return v 176 | } 177 | } 178 | 179 | // MapToSlice returns an array whose elements are generated by iteratee function. 180 | func MapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(k K, v V) R) []R { 181 | result := make([]R, 0, len(in)) 182 | for k, v := range in { 183 | result = append(result, iteratee(k, v)) 184 | } 185 | return result 186 | } 187 | -------------------------------------------------------------------------------- /map_example_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/exp/constraints" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | var exampleMap = map[string]int{ 11 | "apple": 10, 12 | "orange": 12, 13 | "banana": 15, 14 | } 15 | 16 | type entries[K constraints.Ordered, V any] []Entry[K, V] 17 | 18 | func (e entries[K, V]) Len() int { 19 | return len(e) 20 | } 21 | 22 | func (e entries[K, V]) Less(i, j int) bool { 23 | return e[i].Key < e[j].Key 24 | } 25 | 26 | func (e entries[K, V]) Swap(i, j int) { 27 | e[i], e[j] = e[j], e[i] 28 | } 29 | 30 | func sprintSortedMapByKey[K constraints.Ordered, V any](in map[K]V) string { 31 | // transform to entry 32 | es := ToEntries(in) 33 | 34 | // sort 35 | sort.Sort(entries[K, V](es)) 36 | 37 | // format 38 | result := "map[" 39 | itemStrs := make([]string, 0, len(es)) 40 | for _, item := range es { 41 | itemStrs = append(itemStrs, fmt.Sprintf("%+v:%+v", item.Key, item.Value)) 42 | } 43 | result += strings.Join(itemStrs, " ") + "]" 44 | return result 45 | } 46 | 47 | func ExampleOmitBy() { 48 | result := OmitBy(exampleMap, func(key string, value int) bool { 49 | return strings.HasPrefix(key, "or") 50 | }) 51 | fmt.Printf("%+v ", sprintSortedMapByKey(result)) 52 | // Output: 53 | // map[apple:10 banana:15] 54 | } 55 | 56 | func ExampleOmitByKeys() { 57 | result := OmitByKeys(exampleMap, []string{"apple"}) 58 | fmt.Printf("%+v", sprintSortedMapByKey(result)) 59 | // Output: 60 | // map[banana:15 orange:12] 61 | } 62 | 63 | func ExampleOmitByValues() { 64 | result := OmitByValues(exampleMap, []int{10}) 65 | fmt.Printf("%+v", sprintSortedMapByKey(result)) 66 | // Output: 67 | // map[banana:15 orange:12] 68 | } 69 | 70 | func ExamplePickBy() { 71 | result := PickBy(exampleMap, func(key string, value int) bool { 72 | return strings.HasPrefix(key, "or") 73 | }) 74 | fmt.Printf("%+v", sprintSortedMapByKey(result)) 75 | // Output: 76 | // map[orange:12] 77 | } 78 | 79 | func ExamplePickByKeys() { 80 | result := PickByKeys(exampleMap, []string{"apple"}) 81 | fmt.Printf("%+v", sprintSortedMapByKey(result)) 82 | // Output: 83 | // map[apple:10] 84 | } 85 | 86 | func ExamplePickByValues() { 87 | result := PickByValues(exampleMap, []int{10}) 88 | fmt.Printf("%+v", sprintSortedMapByKey(result)) 89 | // Output: 90 | // map[apple:10] 91 | } 92 | 93 | func ExampleToEntries() { 94 | result := ToEntries(exampleMap) 95 | sort.Sort(entries[string, int](result)) 96 | fmt.Printf("%+v", result) 97 | // Output: 98 | // [{Key:apple Value:10} {Key:banana Value:15} {Key:orange Value:12}] 99 | } 100 | 101 | func ExampleFromEntries() { 102 | es := []Entry[string, int]{ 103 | {"wang", 1}, 104 | {"zhao", 3}, 105 | } 106 | result := FromEntries(es) 107 | fmt.Printf("%+v", sprintSortedMapByKey(result)) 108 | // Output: 109 | // map[wang:1 zhao:3] 110 | } 111 | 112 | func ExampleInvert() { 113 | result := Invert(exampleMap) 114 | fmt.Printf("%+v", sprintSortedMapByKey(result)) 115 | // Output: 116 | // map[10:apple 12:orange 15:banana] 117 | } 118 | 119 | func ExampleMapOverMap() { 120 | MapOverMap(exampleMap, func(k string, v int) { 121 | fmt.Println(k, v) 122 | }) 123 | } 124 | 125 | func ExampleMapKeys() { 126 | result := MapKeys(exampleMap, func(k string, v int) string { 127 | return "common_key" 128 | }) 129 | fmt.Printf("%+v", Keys(result)) 130 | // Output: 131 | // [common_key] 132 | } 133 | 134 | func ExampleMapValues() { 135 | result := MapValues(exampleMap, func(k string, v int) int { 136 | return 0 137 | }) 138 | fmt.Printf("%+v", Values(result)) 139 | // Output: 140 | // [0 0 0] 141 | } 142 | 143 | func ExampleMapEntries() { 144 | result := MapEntries(exampleMap, func(k string, v int) (string, int) { 145 | return "common_key", 0 146 | }) 147 | fmt.Printf("%+v", result) 148 | // Output: 149 | // map[common_key:0] 150 | } 151 | 152 | func ExampleMerge() { 153 | result := Merge( 154 | map[string]int{"a": 1, "b": 2}, 155 | map[string]int{"c": 3}, 156 | ) 157 | fmt.Printf("%+v", sprintSortedMapByKey(result)) 158 | // Output: 159 | // map[a:1 b:2 c:3] 160 | } 161 | 162 | func ExampleKeys() { 163 | result := Keys(exampleMap) 164 | fmt.Printf("%+v", len(result)) 165 | // Output: 166 | // 3 167 | } 168 | 169 | func ExampleValues() { 170 | result := Values(exampleMap) 171 | fmt.Printf("%+v", len(result)) 172 | // Output: 173 | // 3 174 | } 175 | 176 | func ExampleValueOr() { 177 | result1 := ValueOr(exampleMap, "apple", 11) 178 | result2 := ValueOr(exampleMap, "bear", 11) 179 | fmt.Printf("%+v %+v", result1, result2) 180 | // Output: 181 | // 10 11 182 | } 183 | 184 | func ExampleMapToSlice() { 185 | result := MapToSlice(exampleMap, func(k string, v int) int64 { 186 | return 1 187 | }) 188 | fmt.Printf("%+v", result) 189 | // Output: 190 | // [1 1 1] 191 | } 192 | -------------------------------------------------------------------------------- /math.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import "golang.org/x/exp/constraints" 4 | 5 | // Range creates a range with abs(elementNum) elements, which starts from 0. 6 | func Range[T constraints.Integer](elementNum int) []T { 7 | length := Ternary(elementNum >= 0, elementNum, -elementNum) 8 | step := Ternary(elementNum >= 0, 1, -1) 9 | result := make([]T, 0, length) 10 | for i, j := 0, T(0); i < length; i, j = i+1, j+T(step) { 11 | result = append(result, j) 12 | } 13 | return result 14 | } 15 | 16 | // RangeFrom creates a range with abs(elementNum) elements, which starts from `from`. 17 | func RangeFrom[T constraints.Integer | constraints.Float](from T, elementNum int) []T { 18 | length := Ternary(elementNum >= 0, elementNum, -elementNum) 19 | step := Ternary(elementNum >= 0, 1, -1) 20 | result := make([]T, 0, length) 21 | for i, j := 0, from; i < length; i, j = i+1, j+T(step) { 22 | result = append(result, j) 23 | } 24 | return result 25 | } 26 | 27 | // RangeWithStep creates a range with abs(elementNum) elements, which starts from `from` and step. 28 | func RangeWithStep[T constraints.Integer | constraints.Float](from, step T, elementNum int) []T { 29 | result := make([]T, 0, elementNum) 30 | for i, j := 0, from; i < elementNum; i, j = i+1, j+step { 31 | result = append(result, j) 32 | } 33 | return result 34 | } 35 | 36 | // Max return maximum value in elements. 37 | func Max[T constraints.Ordered](elements ...T) T { 38 | if len(elements) == 0 { 39 | panic("need at least one elements") 40 | } 41 | return Reduce[T, T](elements[1:], func(agg T, item T, index int) T { 42 | if agg < item { 43 | agg = item 44 | } 45 | return agg 46 | }, elements[0]) 47 | } 48 | 49 | // Min return minimum value in elements. 50 | func Min[T constraints.Ordered](elements ...T) T { 51 | if len(elements) == 0 { 52 | panic("need at least one elements") 53 | } 54 | return Reduce[T, T](elements[1:], func(agg T, item T, index int) T { 55 | if agg > item { 56 | agg = item 57 | } 58 | return agg 59 | }, elements[0]) 60 | } 61 | 62 | // Sum returns sum of collection. 63 | func Sum[T constraints.Integer | constraints.Float | constraints.Complex](collection []T) T { 64 | return Reduce[T, T](collection, func(agg T, item T, index int) T { 65 | return agg + item 66 | }, 0) 67 | } 68 | 69 | // SumBy returns sum of items generated by iteratee function. 70 | func SumBy[T any, R constraints.Integer | constraints.Float | constraints.Complex](collection []T, iteratee func(t T, index int) R) R { 71 | return Reduce[T, R](collection, func(agg R, item T, index int) R { 72 | return agg + iteratee(item, index) 73 | }, 0) 74 | } 75 | -------------------------------------------------------------------------------- /math_example_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import "fmt" 4 | 5 | func ExampleRange() { 6 | result1 := Range[int64](3) 7 | result2 := Range[int64](-3) 8 | fmt.Println(result1) 9 | fmt.Println(result2) 10 | // Output: 11 | // [0 1 2] 12 | // [0 -1 -2] 13 | } 14 | 15 | func ExampleRangeFrom() { 16 | result1 := RangeFrom[int64](8, 3) 17 | result2 := RangeFrom[int64](8, -3) 18 | fmt.Println(result1) 19 | fmt.Println(result2) 20 | // Output: 21 | // [8 9 10] 22 | // [8 7 6] 23 | } 24 | 25 | func ExampleRangeWithStep() { 26 | result1 := RangeWithStep[int64](8, 2, 3) 27 | result2 := RangeWithStep[int64](8, -2, 3) 28 | fmt.Println(result1) 29 | fmt.Println(result2) 30 | // Output: 31 | // [8 10 12] 32 | // [8 6 4] 33 | } 34 | 35 | func ExampleMax() { 36 | result := Max(1, 2, 3, 6, 5) 37 | fmt.Println(result) 38 | // Output: 39 | // 6 40 | } 41 | 42 | func ExampleMin() { 43 | result := Min(7, -1, 3, 6, 7) 44 | fmt.Println(result) 45 | // Output: 46 | // -1 47 | } 48 | 49 | func ExampleSum() { 50 | result := Sum([]int{1, 2, 3, 4, 5}) 51 | fmt.Println(result) 52 | // Output: 53 | // 15 54 | } 55 | 56 | func ExampleSumBy() { 57 | result := SumBy([]int{1, 2, 3, 4, 5}, func(t, index int) int { 58 | return t + 1 59 | }) 60 | fmt.Println(result) 61 | // Output: 62 | // 20 63 | } 64 | -------------------------------------------------------------------------------- /math_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestRange(t *testing.T) { 9 | t.Parallel() 10 | is := assert.New(t) 11 | 12 | result1 := Range[int](0) 13 | is.Equal([]int{}, result1) 14 | 15 | result2 := Range[int](3) 16 | is.Equal([]int{0, 1, 2}, result2) 17 | } 18 | -------------------------------------------------------------------------------- /set.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | func CollectionToSet[T comparable](collection []T) map[T]struct{} { 4 | mp := make(map[T]struct{}) 5 | for _, item := range collection { 6 | mp[item] = struct{}{} 7 | } 8 | return mp 9 | } 10 | -------------------------------------------------------------------------------- /slice.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/spf13/cast" 7 | "math" 8 | "math/rand" 9 | "strings" 10 | ) 11 | 12 | // Filter iterates over elements of collection, returns an array of all elements that predicate function returns truthy for. 13 | func Filter[T any](collection []T, predicate func(item T, index int) bool) []T { 14 | result := make([]T, 0) 15 | for index, item := range collection { 16 | if predicate(item, index) { 17 | result = append(result, item) 18 | } 19 | } 20 | return result 21 | } 22 | 23 | // Reject iterates over elements of collection, returns an array of all elements that predicate function returns falsey for. 24 | func Reject[T any](collection []T, predicate func(item T, index int) bool) []T { 25 | result := make([]T, 0) 26 | for index, item := range collection { 27 | if !predicate(item, index) { 28 | result = append(result, item) 29 | } 30 | } 31 | return result 32 | } 33 | 34 | // Map manipulates a slice and transforms it into a slice with another type. 35 | func Map[T any, R any](collection []T, iteratee func(item T, index int) R) []R { 36 | result := make([]R, 0, len(collection)) 37 | for index, item := range collection { 38 | result = append(result, iteratee(item, index)) 39 | } 40 | return result 41 | } 42 | 43 | // FilterMap returns a slice which obtained after both filtering and mapping using the given callback function. 44 | // The callback function should return two values: 45 | // - the result of the mapping operation and 46 | // - whether the result element should be included or not. 47 | func FilterMap[T any, R any](collection []T, callback func(item T, index int) (R, bool)) []R { 48 | result := make([]R, 0, len(collection)) 49 | for index, item := range collection { 50 | if value, ok := callback(item, index); ok { 51 | result = append(result, value) 52 | } 53 | } 54 | return result 55 | } 56 | 57 | // FlatMap manipulates a slice and transforms and flattens it to a slice of another type. 58 | // The transform function either return a slice or a `nil`. 59 | func FlatMap[T any, R any](collection []T, iteratee func(item T, index int) []R) []R { 60 | result := make([]R, 0) 61 | for index, item := range collection { 62 | result = append(result, iteratee(item, index)...) 63 | } 64 | return result 65 | } 66 | 67 | // Reduce reduces a collection into a value which is the accumulate result of running each element in collection through accumulator, 68 | // where each successive invocation is supplied the return value of the previous. 69 | func Reduce[T any, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R { 70 | for index, item := range collection { 71 | initial = accumulator(initial, item, index) 72 | } 73 | return initial 74 | } 75 | 76 | // ReduceRight helper is like Reduce except that it iterates over elements from right to left. 77 | func ReduceRight[T any, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R { 78 | for index := len(collection) - 1; index >= 0; index-- { 79 | initial = accumulator(initial, collection[index], index) 80 | } 81 | return initial 82 | } 83 | 84 | // ForEach iterates over collection and invokes iteratee function for each element. 85 | func ForEach[T any](collection []T, iteratee func(item T, index int)) { 86 | for index, item := range collection { 87 | iteratee(item, index) 88 | } 89 | } 90 | 91 | // ForEachRight iterates over collection from the end and invokes iteratee function for each element. 92 | func ForEachRight[T any](collection []T, iteratee func(item T, index int)) { 93 | for index := len(collection) - 1; index >= 0; index-- { 94 | iteratee(collection[index], index) 95 | } 96 | } 97 | 98 | // Times invokes the iteratee function n times, returning an array of results of each invocation. 99 | func Times[T any](count int, iteratee func(index int) T) []T { 100 | result := make([]T, 0, count) 101 | for i := 0; i < count; i++ { 102 | result = append(result, iteratee(i)) 103 | } 104 | return result 105 | } 106 | 107 | // Uniq returns a duplicate-free version of an array, in which only the first occurrence of each element is kept. 108 | // The order of result values is determined by the order they occur in the array. 109 | func Uniq[T comparable](collection []T) []T { 110 | result := make([]T, 0) 111 | seen := make(map[T]struct{}) 112 | for _, item := range collection { 113 | if _, ok := seen[item]; !ok { 114 | seen[item] = struct{}{} 115 | result = append(result, item) 116 | } 117 | } 118 | return result 119 | } 120 | 121 | // UniqBy returns a duplicate-free version of an array, in which only the first occurrence of the element is kept. 122 | // It accepts `iteratee` which is invoked for each element in array to generate the criterion by which uniqueness is computed. 123 | // The order of result values is determined by the order they occur in the array. 124 | func UniqBy[T any, U comparable](collection []T, iteratee func(item T, index int) U) []T { 125 | result := make([]T, 0) 126 | seen := make(map[U]struct{}) 127 | for index, item := range collection { 128 | u := iteratee(item, index) 129 | if _, ok := seen[u]; !ok { 130 | seen[u] = struct{}{} 131 | result = append(result, item) 132 | } 133 | } 134 | return result 135 | } 136 | 137 | // GroupBy returns an object composed of keys generated from the results of running each element of collection through iteratee. 138 | func GroupBy[T any, U comparable](collection []T, iteratee func(item T, index int) U) map[U][]T { 139 | result := make(map[U][]T) 140 | for index, item := range collection { 141 | u := iteratee(item, index) 142 | result[u] = append(result[u], item) 143 | } 144 | return result 145 | } 146 | 147 | // Chunk returns an array of elements split into groups the length of size. 148 | // If collection can't be split evenly, the final chunk will be the remaining elements. 149 | func Chunk[T any](collection []T, size int) [][]T { 150 | if size <= 0 { 151 | panic("size should be greater than 0") 152 | } 153 | chunkNum := int(math.Ceil(float64(len(collection)) / float64(size))) 154 | result := make([][]T, chunkNum) 155 | for chunkIndex := 0; chunkIndex < chunkNum; chunkIndex++ { 156 | endIndex := (chunkIndex + 1) * size 157 | if endIndex > len(collection) { 158 | endIndex = len(collection) 159 | } 160 | result[chunkIndex] = collection[chunkIndex*size : endIndex] 161 | } 162 | return result 163 | } 164 | 165 | // PartitionBy returns an array of elements split into groups. 166 | // The order of grouped values is determined by the order they occur in collection. 167 | // The grouping is generated from the results of running each element of collection through iteratee function. 168 | func PartitionBy[T any, P comparable](collection []T, iteratee func(item T, index int) P) [][]T { 169 | result := make([][]T, 0) 170 | seen := make(map[P]int) 171 | for index, item := range collection { 172 | key := iteratee(item, index) 173 | if index, ok := seen[key]; ok { 174 | result[index] = append(result[index], item) 175 | } else { 176 | seen[key] = len(result) 177 | result = append(result, []T{item}) 178 | } 179 | } 180 | return result 181 | } 182 | 183 | // Flatten returns an array of single level deep. 184 | func Flatten[T any](collection [][]T) []T { 185 | result := make([]T, 0) 186 | for _, item := range collection { 187 | result = append(result, item...) 188 | } 189 | return result 190 | } 191 | 192 | // Interleave round-robin alternating input slices and sequentially appending value at index into result. 193 | func Interleave[T any](collections ...[]T) []T { 194 | result := make([]T, 0) 195 | maxLengthCollection := FindBy[[]T](collections, func(a, b []T) bool { 196 | return len(a) > len(b) 197 | }) 198 | maxLength := len(maxLengthCollection) 199 | for lengthIndex := 0; lengthIndex < maxLength; lengthIndex++ { 200 | for _, collection := range collections { 201 | if len(collection)-1 >= lengthIndex { 202 | result = append(result, collection[lengthIndex]) 203 | } 204 | } 205 | } 206 | return result 207 | } 208 | 209 | // Shuffle shuffles the values in the collection in-place. 210 | // Using the Fisher-Yates shuffle algorithm. 211 | func Shuffle[T any](collection []T) []T { 212 | rand.Shuffle(len(collection), func(i, j int) { 213 | collection[i], collection[j] = collection[j], collection[i] 214 | }) 215 | return collection 216 | } 217 | 218 | // Reverse reverses array in-place so that the first element become the last, the second element becomes the second to the last, and so on. 219 | func Reverse[T any](collection []T) []T { 220 | length := len(collection) 221 | half := length / 2 222 | for i := 0; i < half; i++ { 223 | j := length - 1 - i 224 | collection[i], collection[j] = collection[j], collection[i] 225 | } 226 | return collection 227 | } 228 | 229 | // Fill fills the collection with initial value. 230 | func Fill[T any](collection []T, initial T) []T { 231 | for index := range collection { 232 | collection[index] = initial 233 | } 234 | return collection 235 | } 236 | 237 | // FillWithClone fills the collection with cloned initial value. 238 | func FillWithClone[T Cloneable[T]](collection []T, initial T) []T { 239 | for index := range collection { 240 | collection[index] = initial 241 | } 242 | return collection 243 | } 244 | 245 | // Repeat returns a array of count initial values. 246 | func Repeat[T any](count int, initial T) []T { 247 | result := make([]T, 0, count) 248 | for i := 0; i < count; i++ { 249 | result = append(result, initial) 250 | } 251 | return result 252 | } 253 | 254 | // RepeatWithClone returns an array of count initial cloned values. 255 | func RepeatWithClone[T Cloneable[T]](count int, initial T) []T { 256 | result := make([]T, 0, count) 257 | for i := 0; i < count; i++ { 258 | result = append(result, initial.Clone()) 259 | } 260 | return result 261 | } 262 | 263 | // RepeatBy returns an array of count values that generated by predicate function. 264 | func RepeatBy[T any](count int, predicate func(index int) T) []T { 265 | result := make([]T, 0, count) 266 | for i := 0; i < count; i++ { 267 | result = append(result, predicate(i)) 268 | } 269 | return result 270 | } 271 | 272 | // KeyBy transforms a slice to a map based on a pivot callback. 273 | func KeyBy[K comparable, V any](collection []V, iteratee func(v V, index int) K) map[K]V { 274 | result := make(map[K]V) 275 | for index, item := range collection { 276 | result[iteratee(item, index)] = item 277 | } 278 | return result 279 | } 280 | 281 | // Associate transforms a slice to a map whose key-value pairs are generated by transform function. 282 | func Associate[T any, K comparable, V any](collection []T, transform func(t T, index int) (K, V)) map[K]V { 283 | result := make(map[K]V) 284 | for index, item := range collection { 285 | k, v := transform(item, index) 286 | result[k] = v 287 | } 288 | return result 289 | } 290 | 291 | // Drop returns a slice with n elements dropped from the beginning of the collection. 292 | func Drop[T any](collection []T, n int) []T { 293 | if n >= len(collection) { 294 | return make([]T, 0) 295 | } 296 | result := make([]T, 0, len(collection)-n) 297 | result = append(result, collection[n:]...) 298 | return result 299 | } 300 | 301 | // DropRight returns a slice with n elements dropped from the end of the collection. 302 | func DropRight[T any](collection []T, n int) []T { 303 | if n >= len(collection) { 304 | return make([]T, 0) 305 | } 306 | result := make([]T, 0, len(collection)-n) 307 | result = append(result, collection[:len(collection)-n]...) 308 | return result 309 | } 310 | 311 | // DropWhile returns a slice excluding elements dropped from the beginning. 312 | // Elements are dropped until the predicate function returns falsey. 313 | func DropWhile[T any](collection []T, predicate func(item T, index int) bool) []T { 314 | startIndex := 0 315 | for ; startIndex < len(collection); startIndex++ { 316 | if !predicate(collection[startIndex], startIndex) { 317 | break 318 | } 319 | } 320 | return Drop(collection, startIndex) 321 | } 322 | 323 | // DropRightWhile returns a slice excluding elements dropped from the end. 324 | // Elements are dropped until the predicate function returns falsey. 325 | func DropRightWhile[T any](collection []T, predicate func(item T, index int) bool) []T { 326 | endIndex := len(collection) - 1 327 | for ; endIndex >= 0; endIndex-- { 328 | if !predicate(collection[endIndex], endIndex) { 329 | break 330 | } 331 | } 332 | return DropRight(collection, len(collection)-1-endIndex) 333 | } 334 | 335 | // Take creates a slice of n elements taken from the beginning. 336 | func Take[T any](collection []T, n int) []T { 337 | return Slice(collection, 0, n) 338 | } 339 | 340 | // TakeRight creates a slice of n elements taken from the end. 341 | func TakeRight[T any](collection []T, n int) []T { 342 | return Slice(collection, len(collection)-n, len(collection)) 343 | } 344 | 345 | // TakeWhile creates a slice of n elements taken from the beginning. 346 | // Elements are taken until the predicate function returns falsey. 347 | func TakeWhile[T any](collection []T, predicate func(item T, index int) bool) []T { 348 | n := 0 349 | for index, item := range collection { 350 | if !predicate(item, index) { 351 | n = index 352 | break 353 | } 354 | } 355 | return Take(collection, n) 356 | } 357 | 358 | // TakeRightWhile creates a slice of n elements taken from the end. 359 | // Elements are taken until the predicate function returns falsey. 360 | func TakeRightWhile[T any](collection []T, predicate func(item T, index int) bool) []T { 361 | endIndex := len(collection) - 1 362 | for ; endIndex >= 0; endIndex-- { 363 | if !predicate(collection[endIndex], endIndex) { 364 | break 365 | } 366 | } 367 | return TakeRight(collection, len(collection)-1-endIndex) 368 | } 369 | 370 | // Head returns first element in the collection. 371 | // If the collection is empty, return zero value of T and false. 372 | func Head[T any](collection []T) (T, bool) { 373 | if len(collection) > 0 { 374 | return collection[0], true 375 | } 376 | var zero T 377 | return zero, false 378 | } 379 | 380 | // Last returns last element in the collection. 381 | // If the collection is empty, return zero value of T and false. 382 | func Last[T any](collection []T) (T, bool) { 383 | if len(collection) > 0 { 384 | return collection[len(collection)-1], true 385 | } 386 | var zero T 387 | return zero, false 388 | } 389 | 390 | // Initial returns all but the last element of the collection. 391 | func Initial[T any](collection []T) []T { 392 | return DropRight(collection, 1) 393 | } 394 | 395 | // Tail returns all but the first element of the collection. 396 | func Tail[T any](collection []T) []T { 397 | return Drop(collection, 1) 398 | } 399 | 400 | // Join converts all elements in array into a string separated by separator 401 | // TODO(@wangli) thinking about how to check if T is string 402 | func Join[T any](collection []T, separator string) string { 403 | return strings.Join(Map[T, string](collection, func(item T, index int) string { 404 | // efficient any to string 405 | result, err := cast.ToStringE(item) 406 | if err != nil { 407 | return fmt.Sprintf("%v", item) 408 | } 409 | return result 410 | }), separator) 411 | } 412 | 413 | // Remove excludes all excludedValues in collection. 414 | func Remove[T comparable](collection []T, excludedValues ...T) []T { 415 | excludeMap := CollectionToSet(excludedValues) 416 | return Filter[T](collection, func(item T, index int) bool { 417 | _, ok := excludeMap[item] 418 | return !ok 419 | }) 420 | } 421 | 422 | // RemoveBy removes all elements that predicate function returns true in collection. 423 | func RemoveBy[T any](collection []T, predicate func(item T, index int) bool) []T { 424 | return Filter[T](collection, func(item T, index int) bool { 425 | return !predicate(item, index) 426 | }) 427 | } 428 | 429 | // Slice returns a slice of collection from start up to, but not including, end. 430 | // It's like collection[start:end], but will not panic on overflow. 431 | func Slice[T any](collection []T, start, end int) []T { 432 | length := len(collection) 433 | if start >= end || length == 0 { 434 | return make([]T, 0) 435 | } 436 | if start < 0 { 437 | start = 0 438 | } 439 | if start > length-1 { 440 | start = length - 1 441 | } 442 | if end < 0 { 443 | end = 0 444 | } 445 | if end > length { 446 | end = length 447 | } 448 | return collection[start:end] 449 | } 450 | 451 | // SliceWithCopy returns a copy of slice in collection from start up to, but not including, end. 452 | func SliceWithCopy[T any](collection []T, start, end int) []T { 453 | src := Slice(collection, start, end) 454 | dst := make([]T, len(src)) 455 | copy(dst, src) 456 | return dst 457 | } 458 | 459 | // Count returns the number of times of the value. 460 | func Count[T comparable](collection []T, value T) int { 461 | count := 0 462 | for _, item := range collection { 463 | if item == value { 464 | count++ 465 | } 466 | } 467 | return count 468 | } 469 | 470 | // CountBy returns a map composed of keys generated from iteratee. 471 | // The corresponding value of each key is the number of times the key was returned by iteratee. 472 | func CountBy[T any, K comparable](collection []T, iteratee func(item T, index int) K) map[K]int { 473 | result := make(map[K]int) 474 | for index, item := range collection { 475 | k := iteratee(item, index) 476 | result[k]++ 477 | } 478 | return result 479 | } 480 | 481 | // Every returns true if all elements of a subset are contained into a collection or if the subset is empty. 482 | func Every[T comparable](collection []T, subset []T) bool { 483 | if len(subset) <= 1 { 484 | for _, item := range subset { 485 | if !Contains(collection, item) { 486 | return false 487 | } 488 | } 489 | return true 490 | } else { 491 | mp := CollectionToSet(collection) 492 | for _, item := range subset { 493 | if !containsInSet(mp, item) { 494 | return false 495 | } 496 | } 497 | return true 498 | } 499 | } 500 | 501 | // EveryBy returns true if predicate function returns true for all elements in the collection or if the collection is empty. 502 | func EveryBy[T comparable](collection []T, predicate func(item T) bool) bool { 503 | for _, item := range collection { 504 | if !predicate(item) { 505 | return false 506 | } 507 | } 508 | return true 509 | } 510 | 511 | // Contains returns true if an element present in a collection. 512 | func Contains[T comparable](collection []T, element T) bool { 513 | for _, item := range collection { 514 | if item == element { 515 | return true 516 | } 517 | } 518 | return false 519 | } 520 | 521 | func containsInSet[T comparable](set map[T]struct{}, element T) bool { 522 | _, ok := set[element] 523 | return ok 524 | } 525 | 526 | // ContainsBy returns true if predicate function return true. 527 | func ContainsBy[T comparable](collection []T, predicate func(item T) bool) bool { 528 | for _, item := range collection { 529 | if predicate(item) { 530 | return true 531 | } 532 | } 533 | return false 534 | } 535 | 536 | // Some returns true if at lease one element of a subset is contained into a collection. 537 | // If the subset is empty Some returns false. 538 | func Some[T comparable](collection []T, subset []T) bool { 539 | if len(subset) <= 1 { 540 | for _, item := range subset { 541 | if Contains(collection, item) { 542 | return true 543 | } 544 | } 545 | return false 546 | } else { 547 | mp := CollectionToSet(collection) 548 | for _, item := range subset { 549 | if containsInSet(mp, item) { 550 | return true 551 | } 552 | } 553 | return false 554 | } 555 | } 556 | 557 | // SomeBy returns true if predicate function returns true for at least one element in the collection. 558 | // If the subset is empty SomeBy returns false. 559 | func SomeBy[T comparable](collection []T, predicate func(item T) bool) bool { 560 | for _, item := range collection { 561 | if predicate(item) { 562 | return true 563 | } 564 | } 565 | return false 566 | } 567 | 568 | // None returns true if all elements of a subset is NOT contained into a collection or if the subset is empty. 569 | func None[T comparable](collection []T, subset []T) bool { 570 | if len(subset) <= 1 { 571 | for _, item := range subset { 572 | if Contains(collection, item) { 573 | return false 574 | } 575 | } 576 | return true 577 | } else { 578 | mp := CollectionToSet(collection) 579 | for _, item := range subset { 580 | if containsInSet(mp, item) { 581 | return false 582 | } 583 | } 584 | return true 585 | } 586 | } 587 | 588 | // NoneBy returns true if predicate function returns false for all elements in the collection or if the subset is empty. 589 | func NoneBy[T comparable](collection []T, predicate func(item T) bool) bool { 590 | for _, item := range collection { 591 | if predicate(item) { 592 | return false 593 | } 594 | } 595 | return true 596 | } 597 | 598 | // Sample returns a random element in collection. 599 | // If collection is empty, return zero value and false. 600 | func Sample[T any](collection []T) (T, bool) { 601 | if len(collection) == 0 { 602 | var zero T 603 | return zero, false 604 | } 605 | // perm 606 | return collection[rand.Intn(len(collection))], true 607 | } 608 | 609 | // SampleSize returns n random elements with diffent indexes in collection. 610 | // TODO(@wangli) make it more efficient. 611 | func SampleSize[T any](collection []T, n int) []T { 612 | min := Min(len(collection), n) 613 | indexes := rand.Perm(len(collection)) 614 | return RepeatBy(min, func(index int) T { 615 | return collection[indexes[index]] 616 | }) 617 | } 618 | 619 | // Nth returns the element at index `nth` of collection. 620 | // If `nth` is negative, returns `nth` element from the end. 621 | // If `nth` is out of slice bound, returns zero-value of T and error. 622 | func Nth[T any](collection []T, n int) (T, error) { 623 | length := len(collection) 624 | if n >= length || -n > length { 625 | var zero T 626 | return zero, errors.New("out of slice bound") 627 | } 628 | if n < 0 { 629 | n = n + length 630 | } 631 | return collection[n], nil 632 | } 633 | -------------------------------------------------------------------------------- /slice_benchmark_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | var lengths = []int{10, 100, 1000} 11 | 12 | func BenchmarkChunk(b *testing.B) { 13 | 14 | for _, n := range lengths { 15 | strs := genSliceString(n) 16 | b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) { 17 | for i := 0; i < b.N; i++ { 18 | _ = Chunk(strs, 5) 19 | } 20 | }) 21 | } 22 | 23 | for _, n := range lengths { 24 | ints := genSliceInt(n) 25 | b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) { 26 | for i := 0; i < b.N; i++ { 27 | _ = Chunk(ints, 5) 28 | } 29 | }) 30 | } 31 | } 32 | 33 | func genSliceString(n int) []string { 34 | result := make([]string, 0, n) 35 | for i := 0; i < n; i++ { 36 | result = append(result, strconv.Itoa(rand.Intn(100_000))) 37 | } 38 | return result 39 | } 40 | 41 | func genSliceInt(n int) []int { 42 | result := make([]int, 0, n) 43 | for i := 0; i < n; i++ { 44 | result = append(result, rand.Intn(100_000)) 45 | } 46 | return result 47 | } 48 | -------------------------------------------------------------------------------- /slice_example_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | // See https://go.dev/blog/examples 9 | 10 | func ExampleFilter() { 11 | list := []int64{1, 2, 3, 4, 5} 12 | result := Filter(list, func(item int64, index int) bool { 13 | return item%2 == 0 14 | }) 15 | fmt.Printf("%+v", result) 16 | // Output: [2 4] 17 | } 18 | 19 | func ExampleReject() { 20 | list := []int64{1, 2, 3, 4, 5} 21 | result := Reject(list, func(item int64, index int) bool { 22 | return item%2 == 0 23 | }) 24 | fmt.Printf("%+v", result) 25 | // Output: [1 3 5] 26 | } 27 | 28 | func ExampleMap() { 29 | list := []int64{1, 2, 3} 30 | result := Map(list, func(item int64, index int) string { 31 | return strconv.FormatInt(item, 10) 32 | }) 33 | fmt.Printf("%+v", result) 34 | // Output: [1 2 3] 35 | } 36 | 37 | func ExampleFilterMap() { 38 | list := []int64{1, 2, 3, 4, 5} 39 | result := FilterMap(list, func(item int64, index int) (string, bool) { 40 | return strconv.FormatInt(item, 10), item%2 == 0 41 | }) 42 | fmt.Printf("%+v", result) 43 | // Output: [2 4] 44 | } 45 | 46 | func ExampleFlatMap() { 47 | list := []int64{1, 23} 48 | result := FlatMap(list, func(item int64, index int) []string { 49 | return []string{ 50 | strconv.FormatInt(item, 10), 51 | strconv.FormatInt(item, 26), 52 | } 53 | }) 54 | fmt.Printf("%+v", result) 55 | // Output: [1 1 23 n] 56 | } 57 | 58 | func ExampleReduce() { 59 | list := Range[int64](101) 60 | result := Reduce[int64, int64](list, func(agg int64, item int64, index int) int64 { 61 | return agg + item 62 | }, 0) 63 | fmt.Printf("%+v", result) 64 | // Output: 5050 65 | } 66 | 67 | func ExampleReduceRight() { 68 | // reverse list 69 | list := []int64{1, 2, 3, 4, 5} 70 | result := ReduceRight[int64, []int64](list, func(agg []int64, item int64, index int) []int64 { 71 | agg = append(agg, item) 72 | return agg 73 | }, make([]int64, 0)) 74 | fmt.Printf("%+v", result) 75 | // Output: [5 4 3 2 1] 76 | } 77 | 78 | func ExampleForEach() { 79 | list := []int64{1, 2, 3, 4, 5} 80 | ForEach(list, func(item int64, index int) { 81 | fmt.Printf("%d:%d,", index, item) 82 | }) 83 | // Output: 0:1,1:2,2:3,3:4,4:5, 84 | } 85 | 86 | func ExampleForEachRight() { 87 | list := []int64{1, 2, 3, 4, 5} 88 | ForEachRight(list, func(item int64, index int) { 89 | fmt.Printf("%d:%d,", index, item) 90 | }) 91 | // Output: 4:5,3:4,2:3,1:2,0:1, 92 | } 93 | 94 | func ExampleTimes() { 95 | result := Times(5, func(index int) int64 { 96 | return int64(index) 97 | }) 98 | fmt.Printf("%+v", result) 99 | // Output: [0 1 2 3 4] 100 | } 101 | 102 | func ExampleUniq() { 103 | list := []int64{1, 1, 2, 2, 3, 3} 104 | result := Uniq(list) 105 | fmt.Printf("%+v", result) 106 | // Output: [1 2 3] 107 | } 108 | 109 | func ExampleUniqBy() { 110 | list := []int64{1, 2, 3, 4, 5} 111 | result := UniqBy(list, func(item int64, index int) int64 { 112 | return item % 2 113 | }) 114 | fmt.Printf("%+v", result) 115 | // Output: [1 2] 116 | } 117 | 118 | func ExampleGroupBy() { 119 | list := []int64{1, 2, 3, 4, 5} 120 | result := GroupBy(list, func(item int64, index int) int64 { 121 | return item % 2 122 | }) 123 | fmt.Printf("%+v", result) 124 | // Output: map[0:[2 4] 1:[1 3 5]] 125 | } 126 | 127 | func ExampleChunk() { 128 | list := []int64{1, 2, 3, 4, 5} 129 | result := Chunk(list, 2) 130 | fmt.Printf("%+v", result) 131 | // Output: [[1 2] [3 4] [5]] 132 | } 133 | 134 | func ExamplePartitionBy() { 135 | list := []int64{1, 2, 3, 4, 5} 136 | result := PartitionBy(list, func(item int64, index int) bool { 137 | return item%2 == 0 138 | }) 139 | fmt.Printf("%+v", result) 140 | // Output: [[1 3 5] [2 4]] 141 | } 142 | 143 | func ExampleFlatten() { 144 | list := [][]int64{{1, 2}, {3, 4}, {5}} 145 | result := Flatten(list) 146 | fmt.Printf("%+v", result) 147 | // Output: [1 2 3 4 5] 148 | } 149 | 150 | func ExampleInterleave() { 151 | list := [][]int64{{1, 2}, {3, 4}, {5}, {6, 7, 8}} 152 | result := Interleave(list...) 153 | fmt.Printf("%+v", result) 154 | // Output: [1 3 5 6 2 4 7 8] 155 | } 156 | 157 | func ExampleShuffle() { 158 | list := []int64{1, 2, 3, 4, 5} 159 | result := Shuffle(list) 160 | fmt.Printf("%+v", len(result)) 161 | // Output: 5 162 | } 163 | 164 | func ExampleReverse() { 165 | list := []int64{1, 2, 3, 4, 5} 166 | result := Reverse(list) 167 | fmt.Printf("%+v", result) 168 | // Output: [5 4 3 2 1] 169 | } 170 | 171 | type cloneableInt64 int64 172 | 173 | func (c cloneableInt64) Clone() cloneableInt64 { 174 | return cloneableInt64(c) 175 | } 176 | 177 | func ExampleFill() { 178 | list := []int64{1, 2, 3, 4, 5} 179 | result := Fill(list, 0) 180 | fmt.Printf("%+v", result) 181 | // Output: [0 0 0 0 0] 182 | } 183 | 184 | func ExampleFillWithClone() { 185 | list := []cloneableInt64{1, 2, 3, 4, 5} 186 | result := FillWithClone[cloneableInt64](list, cloneableInt64(0)) 187 | fmt.Printf("%+v", result) 188 | // Output: [0 0 0 0 0] 189 | } 190 | 191 | func ExampleRepeat() { 192 | result := Repeat(5, 5) 193 | fmt.Printf("%+v", result) 194 | // Output: [5 5 5 5 5] 195 | } 196 | 197 | func ExampleRepeatWithClone() { 198 | result := RepeatWithClone(5, cloneableInt64(5)) 199 | fmt.Printf("%+v", result) 200 | // Output: [5 5 5 5 5] 201 | } 202 | 203 | func ExampleRepeatBy() { 204 | result := RepeatBy(5, func(index int) int64 { 205 | return int64(index) 206 | }) 207 | fmt.Printf("%+v", result) 208 | // Output: [0 1 2 3 4] 209 | } 210 | 211 | func ExampleKeyBy() { 212 | list := []int64{1, 2, 3, 4, 5} 213 | result := KeyBy(list, func(item int64, index int) string { 214 | return fmt.Sprintf("key-%d", item) 215 | }) 216 | fmt.Printf("%+v", result) 217 | // Output: map[key-1:1 key-2:2 key-3:3 key-4:4 key-5:5] 218 | } 219 | 220 | func ExampleAssociate() { 221 | list := []int64{1, 2, 3, 4, 5} 222 | result := Associate(list, func(item int64, index int) (string, string) { 223 | return fmt.Sprintf("key-%d", item), fmt.Sprintf("value-%d", index) 224 | }) 225 | fmt.Printf("%+v", result) 226 | // Output: map[key-1:value-0 key-2:value-1 key-3:value-2 key-4:value-3 key-5:value-4] 227 | } 228 | 229 | func ExampleDrop() { 230 | list := []int64{1, 2, 3, 4, 5} 231 | result := Drop(list, 2) 232 | fmt.Printf("%+v", result) 233 | // Output: [3 4 5] 234 | } 235 | 236 | func ExampleDropRight() { 237 | list := []int64{1, 2, 3, 4, 5} 238 | result := DropRight(list, 2) 239 | fmt.Printf("%+v", result) 240 | // Output: [1 2 3] 241 | } 242 | 243 | func ExampleDropWhile() { 244 | list := []int64{1, 2, 3, 4, 5} 245 | result := DropWhile(list, func(item int64, index int) bool { 246 | return item < 3 247 | }) 248 | fmt.Printf("%+v", result) 249 | // Output: [3 4 5] 250 | } 251 | 252 | func ExampleDropRightWhile() { 253 | list := []int64{1, 2, 3, 4, 5} 254 | result := DropRightWhile(list, func(item int64, index int) bool { 255 | return item > 3 256 | }) 257 | fmt.Printf("%+v", result) 258 | // Output: [1 2 3] 259 | } 260 | 261 | func ExampleTake() { 262 | list := []int64{1, 2, 3, 4, 5} 263 | result := Take(list, 2) 264 | fmt.Printf("%+v", result) 265 | // Output: [1 2] 266 | } 267 | 268 | func ExampleTakeRight() { 269 | list := []int64{1, 2, 3, 4, 5} 270 | result := TakeRight(list, 2) 271 | fmt.Printf("%+v", result) 272 | // Output: [4 5] 273 | } 274 | 275 | func ExampleTakeWhile() { 276 | list := []int64{1, 2, 3, 4, 5} 277 | result := TakeWhile(list, func(item int64, index int) bool { 278 | return item < 3 279 | }) 280 | fmt.Printf("%+v", result) 281 | // Output: [1 2] 282 | } 283 | 284 | func ExampleTakeRightWhile() { 285 | list := []int64{1, 2, 3, 4, 5} 286 | result := TakeRightWhile(list, func(item int64, index int) bool { 287 | return item > 3 288 | }) 289 | fmt.Printf("%+v", result) 290 | // Output: [4 5] 291 | } 292 | 293 | func ExampleHead() { 294 | list := []int64{1, 2, 3, 4, 5} 295 | result, ok := Head(list) 296 | fmt.Printf("%+v\n", result) 297 | fmt.Printf("%+v\n", ok) 298 | // Output: 299 | // 1 300 | // true 301 | } 302 | 303 | func ExampleLast() { 304 | list := []int64{1, 2, 3, 4, 5} 305 | result, ok := Last(list) 306 | fmt.Printf("%+v\n", result) 307 | fmt.Printf("%+v\n", ok) 308 | // Output: 309 | // 5 310 | // true 311 | } 312 | 313 | func ExampleInitial() { 314 | list := []int64{1, 2, 3, 4, 5} 315 | result := Initial(list) 316 | fmt.Printf("%+v", result) 317 | // Output: [1 2 3 4] 318 | } 319 | 320 | func ExampleTail() { 321 | list := []int64{1, 2, 3, 4, 5} 322 | result := Tail(list) 323 | fmt.Printf("%+v", result) 324 | // Output: [2 3 4 5] 325 | } 326 | 327 | func ExampleJoin() { 328 | list := []int64{1, 2, 3, 4, 5} 329 | result := Join(list, ",") 330 | fmt.Printf("%+v", result) 331 | // Output: 1,2,3,4,5 332 | } 333 | 334 | func ExampleRemove() { 335 | list := []int64{1, 2, 3, 4, 5} 336 | result := Remove(list, 1, 3) 337 | fmt.Printf("%+v", result) 338 | // Output: [2 4 5] 339 | } 340 | 341 | func ExampleRemoveBy() { 342 | list := []int64{1, 2, 3, 4, 5} 343 | result := RemoveBy(list, func(item int64, index int) bool { 344 | return item%2 == 0 345 | }) 346 | fmt.Printf("%+v", result) 347 | // Output: [1 3 5] 348 | } 349 | 350 | func ExampleSlice() { 351 | list := []int64{1, 2, 3, 4, 5} 352 | result := Slice(list, 1, 3) 353 | fmt.Printf("%+v", result) 354 | // Output: [2 3] 355 | } 356 | 357 | func ExampleSliceWithCopy() { 358 | list := []int64{1, 2, 3, 4, 5} 359 | result := SliceWithCopy(list, 1, 3) 360 | fmt.Printf("%+v", result) 361 | // Output: [2 3] 362 | } 363 | 364 | func ExampleSampleSize() { 365 | list := []int64{1, 2, 3, 4, 5, 6, 7} 366 | result := SampleSize(list, 2) 367 | fmt.Printf("%+v", len(result)) 368 | // Output: 2 369 | } 370 | 371 | func ExampleNth() { 372 | collection := []int{1, 2, 3, 4, 5} 373 | result1, err1 := Nth(collection, 1) 374 | result2, err2 := Nth(collection, -1) 375 | fmt.Println(result1, err1, result2, err2) 376 | // Output: 377 | // 2 5 378 | } 379 | -------------------------------------------------------------------------------- /slice_fuzz_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | // See https://go.dev/security/fuzz/ 10 | 11 | func FuzzReverse(f *testing.F) { 12 | f.Add([]byte{1, 3, 4, 5, 6}) 13 | f.Fuzz(func(t *testing.T, list []byte) { 14 | listCopyed := make([]byte, len(list)) 15 | copy(listCopyed, list) 16 | Reverse(list) 17 | Reverse(list) 18 | assert.Equal(t, listCopyed, list) 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /slice_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | // TODO(@wangli) edge case 9 | 10 | func TestFilter(t *testing.T) { 11 | t.Parallel() 12 | is := assert.New(t) 13 | 14 | result1 := Filter([]int{1, 2, 3, 4, 5}, func(item int, index int) bool { 15 | return item%2 == 0 16 | }) 17 | is.Equal([]int{2, 4}, result1) 18 | 19 | result2 := Filter([]int{}, func(item int, index int) bool { 20 | return item%2 == 0 21 | }) 22 | is.Equal([]int{}, result2) 23 | } 24 | 25 | func TestContains(t *testing.T) { 26 | t.Parallel() 27 | is := assert.New(t) 28 | 29 | result1 := Contains([]int{1, 2, 3, 4, 5}, 5) 30 | result2 := Contains([]int{1, 2, 3, 4, 5}, 6) 31 | 32 | is.True(result1) 33 | is.False(result2) 34 | } 35 | 36 | func TestContainsBy(t *testing.T) { 37 | t.Parallel() 38 | is := assert.New(t) 39 | 40 | type a struct { 41 | A int 42 | B string 43 | } 44 | 45 | data := []a{{A: 1, B: "a"}, {A: 2, B: "B"}, {A: 3, B: "C"}} 46 | result1 := ContainsBy(data, func(item a) bool { return item.A == 1 && item.B == "a" }) 47 | result2 := ContainsBy(data, func(item a) bool { return item.A == 1 && item.B == "B" }) 48 | 49 | is.True(result1) 50 | is.False(result2) 51 | } 52 | 53 | func TestEvery(t *testing.T) { 54 | t.Parallel() 55 | is := assert.New(t) 56 | 57 | result1 := Every([]int{1, 2, 3, 4, 5}, []int{2}) 58 | result2 := Every([]int{1, 2, 3, 4, 5}, []int{2, 3, 4}) 59 | result3 := Every([]int{1, 2, 3, 4, 5}, []int{23, 3, 4}) 60 | 61 | is.True(result1) 62 | is.True(result2) 63 | is.False(result3) 64 | } 65 | 66 | func TestEveryBy(t *testing.T) { 67 | t.Parallel() 68 | is := assert.New(t) 69 | 70 | result1 := EveryBy([]int{1, 2, 3, 4, 5}, func(item int) bool { return item <= 6 }) 71 | result2 := EveryBy([]int{1, 2, 3, 4, 5, 7}, func(item int) bool { return item <= 6 }) 72 | 73 | is.True(result1) 74 | is.False(result2) 75 | } 76 | 77 | func TestSome(t *testing.T) { 78 | t.Parallel() 79 | is := assert.New(t) 80 | 81 | result1 := Some([]int{1, 2, 3, 4, 5}, []int{2}) 82 | result2 := Some([]int{1, 2, 3, 4, 5}, []int{2, 3, 6}) 83 | result3 := Some([]int{1, 2, 3, 4, 5}, []int{6, 7, 8}) 84 | 85 | is.True(result1) 86 | is.True(result2) 87 | is.False(result3) 88 | } 89 | 90 | func TestSomeBy(t *testing.T) { 91 | t.Parallel() 92 | is := assert.New(t) 93 | 94 | result1 := SomeBy([]int{1, 2, 3, 4, 5}, func(item int) bool { return item <= 3 }) 95 | result2 := SomeBy([]int{1, 2, 3, 4, 5, 7}, func(item int) bool { return item <= -1 }) 96 | 97 | is.True(result1) 98 | is.False(result2) 99 | } 100 | 101 | func TestNone(t *testing.T) { 102 | t.Parallel() 103 | is := assert.New(t) 104 | 105 | result1 := None([]int{1, 2, 3, 4, 5}, []int{2}) 106 | result2 := None([]int{1, 2, 3, 4, 5}, []int{2, 3, 6}) 107 | result3 := None([]int{1, 2, 3, 4, 5}, []int{6, 7, 8}) 108 | 109 | is.False(result1) 110 | is.False(result2) 111 | is.True(result3) 112 | } 113 | 114 | func TestNoneBy(t *testing.T) { 115 | t.Parallel() 116 | is := assert.New(t) 117 | 118 | result1 := NoneBy([]int{1, 2, 3, 4, 5}, func(item int) bool { return item <= 3 }) 119 | result2 := NoneBy([]int{1, 2, 3, 4, 5, 7}, func(item int) bool { return item <= -1 }) 120 | 121 | is.False(result1) 122 | is.True(result2) 123 | } 124 | -------------------------------------------------------------------------------- /string.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "math" 5 | "math/rand" 6 | "regexp" 7 | "strings" 8 | "unicode/utf8" 9 | ) 10 | 11 | var ( 12 | LowerCaseLettersCharset = []rune("abcdefghijklmnopqrstuvwxyz") 13 | UpperCaseLettersCharset = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ") 14 | LettersCharset = append(LowerCaseLettersCharset, UpperCaseLettersCharset...) 15 | NumbersCharset = []rune("0123456789") 16 | AlphanumericCharset = append(LettersCharset, NumbersCharset...) 17 | SpecialCharset = []rune("!@#$%^&*()_+-=[]{}|;':\",./<>?") 18 | AllCharset = append(AlphanumericCharset, SpecialCharset...) 19 | 20 | wordsReg = regexp.MustCompile(`\b\w+\b`) 21 | ) 22 | 23 | // RandomString returns string of utf8-length size, and with char from charset. 24 | func RandomString(size int, charset []rune) string { 25 | if size < 0 { 26 | panic("RandomString: size should greater than 0") 27 | } 28 | if len(charset) == 0 { 29 | panic("charset should not be empty") 30 | } 31 | result := make([]rune, 0, size) 32 | length := len(charset) 33 | for i := 0; i < size; i++ { 34 | result = append(result, charset[rand.Intn(length)]) 35 | } 36 | return string(result) 37 | } 38 | 39 | // SubString returns result with offset and size. 40 | func SubString[T ~string](str T, offset int, size uint) T { 41 | length := len(str) 42 | if length == 0 { 43 | return "" 44 | } 45 | if offset < 0 { 46 | offset = length + offset 47 | if offset < 0 { 48 | offset = 0 49 | } 50 | } 51 | rightIndex := int(Min(uint(length), uint(offset)+size)) 52 | return str[offset:rightIndex] 53 | } 54 | 55 | // ChunkString returns chunks whose lengths are len(size), and from str. 56 | func ChunkString[T ~string](str T, size int) []T { 57 | if size <= 0 { 58 | panic("size should be greater than 0") 59 | } 60 | length := len(str) 61 | result := make([]T, int(math.Ceil(float64(len(str))/float64(size)))) 62 | for index := range result { 63 | result[index] = str[index*size : Min((index+1)*size, length)] 64 | } 65 | return result 66 | } 67 | 68 | // RuneLength returns rune count in str. 69 | func RuneLength(str string) int { 70 | return utf8.RuneCountInString(str) 71 | } 72 | 73 | // SnakeCase returns snake-case version of input string. 74 | func SnakeCase(raw string) string { 75 | words := Words(raw) 76 | titledWords := Map(words, func(item string, index int) string { 77 | return strings.ToLower(item) 78 | }) 79 | return strings.Join(titledWords, "_") 80 | } 81 | 82 | // CamelCase returns camel-case version of input string. 83 | func CamelCase(raw string) string { 84 | words := Words(raw) 85 | titledWords := Map(words, func(item string, index int) string { 86 | if index == 0 { 87 | return strings.ToLower(item) 88 | } else { 89 | return strings.Title(strings.ToLower(item)) 90 | } 91 | }) 92 | return strings.Join(titledWords, "") 93 | } 94 | 95 | // Capitalize converts the first character of string to upper case, 96 | // and the remaining to lower case. 97 | func Capitalize(raw string) string { 98 | if len(raw) == 0 { 99 | return raw 100 | } 101 | raw = strings.ToLower(raw) 102 | rawRune := []rune(raw) 103 | rawRune[0] = []rune(strings.ToUpper(string(rawRune[0])))[0] 104 | return string(rawRune) 105 | } 106 | 107 | // RepeatString repeats the string n times. 108 | func RepeatString(raw string, n int) string { 109 | if raw == "" { 110 | return "" 111 | } 112 | result := make([]string, n) 113 | ForEach(result, func(item string, index int) { 114 | result[index] = raw 115 | }) 116 | return strings.Join(result, "") 117 | } 118 | 119 | // Words splits string into an array of its words. 120 | func Words(raw string) []string { 121 | return wordsReg.FindAllString(raw, -1) 122 | } 123 | -------------------------------------------------------------------------------- /string_example_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import "fmt" 4 | 5 | func ExampleRandomString() { 6 | result := RandomString(10, AllCharset) 7 | fmt.Println(len(result)) 8 | // Output: 9 | // 10 10 | } 11 | 12 | func ExampleSubString() { 13 | result1 := SubString("abcde", -2, 2) 14 | result2 := SubString("abcde", 0, 10) 15 | fmt.Println(result1, result2) 16 | // Output: 17 | // de abcde 18 | } 19 | 20 | func ExampleChunkString() { 21 | result := ChunkString("abcde", 2) 22 | fmt.Println(result) 23 | // Output: 24 | // [ab cd e] 25 | } 26 | 27 | func ExampleRuneLength() { 28 | result := RuneLength("王hha") 29 | fmt.Println(result) 30 | // Output: 31 | // 4 32 | } 33 | 34 | func ExampleSnakeCase() { 35 | result := SnakeCase("hello, world!") 36 | fmt.Println(result) 37 | // Output: 38 | // hello_world 39 | } 40 | 41 | func ExampleCamelCase() { 42 | result := CamelCase("hello, world!") 43 | fmt.Println(result) 44 | // Output: 45 | // helloWorld 46 | } 47 | 48 | func ExampleCapitalize() { 49 | result := Capitalize("hello, world!") 50 | fmt.Println(result) 51 | // Output: 52 | // Hello, world! 53 | } 54 | 55 | func ExampleRepeatString() { 56 | result := RepeatString("ab", 3) 57 | fmt.Println(result) 58 | // Output: 59 | // ababab 60 | } 61 | 62 | func ExampleWords() { 63 | result := Words("hello, world!") 64 | fmt.Println(result) 65 | // Output: 66 | // [hello world] 67 | } 68 | -------------------------------------------------------------------------------- /string_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestCamelCase(t *testing.T) { 9 | t.Parallel() 10 | is := assert.New(t) 11 | 12 | raw1 := "hello, world! It's wonderful." 13 | is.Equal("helloWorldItSWonderful", CamelCase(raw1)) 14 | } 15 | -------------------------------------------------------------------------------- /toolbox_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | -------------------------------------------------------------------------------- /tuples.go: -------------------------------------------------------------------------------- 1 | // Code generated by go run cmd/generate_tuples.go -output tuples.go 2 | // DO NOT EDIT 3 | 4 | package toolbox 5 | 6 | 7 | type Tuple2[T1 any, T2 any] struct { 8 | A T1 9 | B T2 10 | } 11 | 12 | // Pack2 returns a Tuple2 instance. 13 | func Pack2[T1 any, T2 any](a T1, b T2) Tuple2[T1, T2] { 14 | return Tuple2[T1, T2]{ A: a, B: b } 15 | } 16 | 17 | // Unpack2 returns a Tuple2's inner value. 18 | func Unpack2[T1 any, T2 any](t2 Tuple2[T1, T2]) (T1, T2) { 19 | return t2.A, t2.B 20 | } 21 | 22 | // Zip2 returns a Tuple2 slice, whose length is max of input collections. 23 | func Zip2[T1 any, T2 any](collection1 []T1, collection2 []T2) []Tuple2[T1, T2] { 24 | maxLength := Max(len(collection1), len(collection2)) 25 | result := make([]Tuple2[T1, T2], maxLength) 26 | for index := 0; index < maxLength; index++ { 27 | a, _ := Nth(collection1, index) 28 | b, _ := Nth(collection2, index) 29 | result[index] = Tuple2[T1, T2]{ A: a, B: b } 30 | } 31 | return result 32 | } 33 | 34 | // Unzip2 returns 2 slices, whose elements come from Tuple2-collection. 35 | func Unzip2[T1 any, T2 any](collection []Tuple2[T1, T2]) ([]T1, []T2) { 36 | length := len(collection) 37 | result1 := make([]T1, length) 38 | result2 := make([]T2, length) 39 | for index := 0; index < length; index++ { 40 | result1[index] = collection[index].A 41 | result2[index] = collection[index].B 42 | } 43 | return result1, result2 44 | } 45 | 46 | type Tuple3[T1 any, T2 any, T3 any] struct { 47 | A T1 48 | B T2 49 | C T3 50 | } 51 | 52 | // Pack3 returns a Tuple3 instance. 53 | func Pack3[T1 any, T2 any, T3 any](a T1, b T2, c T3) Tuple3[T1, T2, T3] { 54 | return Tuple3[T1, T2, T3]{ A: a, B: b, C: c } 55 | } 56 | 57 | // Unpack3 returns a Tuple3's inner value. 58 | func Unpack3[T1 any, T2 any, T3 any](t3 Tuple3[T1, T2, T3]) (T1, T2, T3) { 59 | return t3.A, t3.B, t3.C 60 | } 61 | 62 | // Zip3 returns a Tuple3 slice, whose length is max of input collections. 63 | func Zip3[T1 any, T2 any, T3 any](collection1 []T1, collection2 []T2, collection3 []T3) []Tuple3[T1, T2, T3] { 64 | maxLength := Max(len(collection1), len(collection2), len(collection3)) 65 | result := make([]Tuple3[T1, T2, T3], maxLength) 66 | for index := 0; index < maxLength; index++ { 67 | a, _ := Nth(collection1, index) 68 | b, _ := Nth(collection2, index) 69 | c, _ := Nth(collection3, index) 70 | result[index] = Tuple3[T1, T2, T3]{ A: a, B: b, C: c } 71 | } 72 | return result 73 | } 74 | 75 | // Unzip3 returns 3 slices, whose elements come from Tuple3-collection. 76 | func Unzip3[T1 any, T2 any, T3 any](collection []Tuple3[T1, T2, T3]) ([]T1, []T2, []T3) { 77 | length := len(collection) 78 | result1 := make([]T1, length) 79 | result2 := make([]T2, length) 80 | result3 := make([]T3, length) 81 | for index := 0; index < length; index++ { 82 | result1[index] = collection[index].A 83 | result2[index] = collection[index].B 84 | result3[index] = collection[index].C 85 | } 86 | return result1, result2, result3 87 | } 88 | 89 | type Tuple4[T1 any, T2 any, T3 any, T4 any] struct { 90 | A T1 91 | B T2 92 | C T3 93 | D T4 94 | } 95 | 96 | // Pack4 returns a Tuple4 instance. 97 | func Pack4[T1 any, T2 any, T3 any, T4 any](a T1, b T2, c T3, d T4) Tuple4[T1, T2, T3, T4] { 98 | return Tuple4[T1, T2, T3, T4]{ A: a, B: b, C: c, D: d } 99 | } 100 | 101 | // Unpack4 returns a Tuple4's inner value. 102 | func Unpack4[T1 any, T2 any, T3 any, T4 any](t4 Tuple4[T1, T2, T3, T4]) (T1, T2, T3, T4) { 103 | return t4.A, t4.B, t4.C, t4.D 104 | } 105 | 106 | // Zip4 returns a Tuple4 slice, whose length is max of input collections. 107 | func Zip4[T1 any, T2 any, T3 any, T4 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4) []Tuple4[T1, T2, T3, T4] { 108 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4)) 109 | result := make([]Tuple4[T1, T2, T3, T4], maxLength) 110 | for index := 0; index < maxLength; index++ { 111 | a, _ := Nth(collection1, index) 112 | b, _ := Nth(collection2, index) 113 | c, _ := Nth(collection3, index) 114 | d, _ := Nth(collection4, index) 115 | result[index] = Tuple4[T1, T2, T3, T4]{ A: a, B: b, C: c, D: d } 116 | } 117 | return result 118 | } 119 | 120 | // Unzip4 returns 4 slices, whose elements come from Tuple4-collection. 121 | func Unzip4[T1 any, T2 any, T3 any, T4 any](collection []Tuple4[T1, T2, T3, T4]) ([]T1, []T2, []T3, []T4) { 122 | length := len(collection) 123 | result1 := make([]T1, length) 124 | result2 := make([]T2, length) 125 | result3 := make([]T3, length) 126 | result4 := make([]T4, length) 127 | for index := 0; index < length; index++ { 128 | result1[index] = collection[index].A 129 | result2[index] = collection[index].B 130 | result3[index] = collection[index].C 131 | result4[index] = collection[index].D 132 | } 133 | return result1, result2, result3, result4 134 | } 135 | 136 | type Tuple5[T1 any, T2 any, T3 any, T4 any, T5 any] struct { 137 | A T1 138 | B T2 139 | C T3 140 | D T4 141 | E T5 142 | } 143 | 144 | // Pack5 returns a Tuple5 instance. 145 | func Pack5[T1 any, T2 any, T3 any, T4 any, T5 any](a T1, b T2, c T3, d T4, e T5) Tuple5[T1, T2, T3, T4, T5] { 146 | return Tuple5[T1, T2, T3, T4, T5]{ A: a, B: b, C: c, D: d, E: e } 147 | } 148 | 149 | // Unpack5 returns a Tuple5's inner value. 150 | func Unpack5[T1 any, T2 any, T3 any, T4 any, T5 any](t5 Tuple5[T1, T2, T3, T4, T5]) (T1, T2, T3, T4, T5) { 151 | return t5.A, t5.B, t5.C, t5.D, t5.E 152 | } 153 | 154 | // Zip5 returns a Tuple5 slice, whose length is max of input collections. 155 | func Zip5[T1 any, T2 any, T3 any, T4 any, T5 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5) []Tuple5[T1, T2, T3, T4, T5] { 156 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5)) 157 | result := make([]Tuple5[T1, T2, T3, T4, T5], maxLength) 158 | for index := 0; index < maxLength; index++ { 159 | a, _ := Nth(collection1, index) 160 | b, _ := Nth(collection2, index) 161 | c, _ := Nth(collection3, index) 162 | d, _ := Nth(collection4, index) 163 | e, _ := Nth(collection5, index) 164 | result[index] = Tuple5[T1, T2, T3, T4, T5]{ A: a, B: b, C: c, D: d, E: e } 165 | } 166 | return result 167 | } 168 | 169 | // Unzip5 returns 5 slices, whose elements come from Tuple5-collection. 170 | func Unzip5[T1 any, T2 any, T3 any, T4 any, T5 any](collection []Tuple5[T1, T2, T3, T4, T5]) ([]T1, []T2, []T3, []T4, []T5) { 171 | length := len(collection) 172 | result1 := make([]T1, length) 173 | result2 := make([]T2, length) 174 | result3 := make([]T3, length) 175 | result4 := make([]T4, length) 176 | result5 := make([]T5, length) 177 | for index := 0; index < length; index++ { 178 | result1[index] = collection[index].A 179 | result2[index] = collection[index].B 180 | result3[index] = collection[index].C 181 | result4[index] = collection[index].D 182 | result5[index] = collection[index].E 183 | } 184 | return result1, result2, result3, result4, result5 185 | } 186 | 187 | type Tuple6[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any] struct { 188 | A T1 189 | B T2 190 | C T3 191 | D T4 192 | E T5 193 | F T6 194 | } 195 | 196 | // Pack6 returns a Tuple6 instance. 197 | func Pack6[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any](a T1, b T2, c T3, d T4, e T5, f T6) Tuple6[T1, T2, T3, T4, T5, T6] { 198 | return Tuple6[T1, T2, T3, T4, T5, T6]{ A: a, B: b, C: c, D: d, E: e, F: f } 199 | } 200 | 201 | // Unpack6 returns a Tuple6's inner value. 202 | func Unpack6[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any](t6 Tuple6[T1, T2, T3, T4, T5, T6]) (T1, T2, T3, T4, T5, T6) { 203 | return t6.A, t6.B, t6.C, t6.D, t6.E, t6.F 204 | } 205 | 206 | // Zip6 returns a Tuple6 slice, whose length is max of input collections. 207 | func Zip6[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6) []Tuple6[T1, T2, T3, T4, T5, T6] { 208 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6)) 209 | result := make([]Tuple6[T1, T2, T3, T4, T5, T6], maxLength) 210 | for index := 0; index < maxLength; index++ { 211 | a, _ := Nth(collection1, index) 212 | b, _ := Nth(collection2, index) 213 | c, _ := Nth(collection3, index) 214 | d, _ := Nth(collection4, index) 215 | e, _ := Nth(collection5, index) 216 | f, _ := Nth(collection6, index) 217 | result[index] = Tuple6[T1, T2, T3, T4, T5, T6]{ A: a, B: b, C: c, D: d, E: e, F: f } 218 | } 219 | return result 220 | } 221 | 222 | // Unzip6 returns 6 slices, whose elements come from Tuple6-collection. 223 | func Unzip6[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any](collection []Tuple6[T1, T2, T3, T4, T5, T6]) ([]T1, []T2, []T3, []T4, []T5, []T6) { 224 | length := len(collection) 225 | result1 := make([]T1, length) 226 | result2 := make([]T2, length) 227 | result3 := make([]T3, length) 228 | result4 := make([]T4, length) 229 | result5 := make([]T5, length) 230 | result6 := make([]T6, length) 231 | for index := 0; index < length; index++ { 232 | result1[index] = collection[index].A 233 | result2[index] = collection[index].B 234 | result3[index] = collection[index].C 235 | result4[index] = collection[index].D 236 | result5[index] = collection[index].E 237 | result6[index] = collection[index].F 238 | } 239 | return result1, result2, result3, result4, result5, result6 240 | } 241 | 242 | type Tuple7[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any] struct { 243 | A T1 244 | B T2 245 | C T3 246 | D T4 247 | E T5 248 | F T6 249 | G T7 250 | } 251 | 252 | // Pack7 returns a Tuple7 instance. 253 | func Pack7[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7) Tuple7[T1, T2, T3, T4, T5, T6, T7] { 254 | return Tuple7[T1, T2, T3, T4, T5, T6, T7]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g } 255 | } 256 | 257 | // Unpack7 returns a Tuple7's inner value. 258 | func Unpack7[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any](t7 Tuple7[T1, T2, T3, T4, T5, T6, T7]) (T1, T2, T3, T4, T5, T6, T7) { 259 | return t7.A, t7.B, t7.C, t7.D, t7.E, t7.F, t7.G 260 | } 261 | 262 | // Zip7 returns a Tuple7 slice, whose length is max of input collections. 263 | func Zip7[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7) []Tuple7[T1, T2, T3, T4, T5, T6, T7] { 264 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7)) 265 | result := make([]Tuple7[T1, T2, T3, T4, T5, T6, T7], maxLength) 266 | for index := 0; index < maxLength; index++ { 267 | a, _ := Nth(collection1, index) 268 | b, _ := Nth(collection2, index) 269 | c, _ := Nth(collection3, index) 270 | d, _ := Nth(collection4, index) 271 | e, _ := Nth(collection5, index) 272 | f, _ := Nth(collection6, index) 273 | g, _ := Nth(collection7, index) 274 | result[index] = Tuple7[T1, T2, T3, T4, T5, T6, T7]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g } 275 | } 276 | return result 277 | } 278 | 279 | // Unzip7 returns 7 slices, whose elements come from Tuple7-collection. 280 | func Unzip7[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any](collection []Tuple7[T1, T2, T3, T4, T5, T6, T7]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7) { 281 | length := len(collection) 282 | result1 := make([]T1, length) 283 | result2 := make([]T2, length) 284 | result3 := make([]T3, length) 285 | result4 := make([]T4, length) 286 | result5 := make([]T5, length) 287 | result6 := make([]T6, length) 288 | result7 := make([]T7, length) 289 | for index := 0; index < length; index++ { 290 | result1[index] = collection[index].A 291 | result2[index] = collection[index].B 292 | result3[index] = collection[index].C 293 | result4[index] = collection[index].D 294 | result5[index] = collection[index].E 295 | result6[index] = collection[index].F 296 | result7[index] = collection[index].G 297 | } 298 | return result1, result2, result3, result4, result5, result6, result7 299 | } 300 | 301 | type Tuple8[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any] struct { 302 | A T1 303 | B T2 304 | C T3 305 | D T4 306 | E T5 307 | F T6 308 | G T7 309 | H T8 310 | } 311 | 312 | // Pack8 returns a Tuple8 instance. 313 | func Pack8[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7, h T8) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { 314 | return Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h } 315 | } 316 | 317 | // Unpack8 returns a Tuple8's inner value. 318 | func Unpack8[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any](t8 Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) (T1, T2, T3, T4, T5, T6, T7, T8) { 319 | return t8.A, t8.B, t8.C, t8.D, t8.E, t8.F, t8.G, t8.H 320 | } 321 | 322 | // Zip8 returns a Tuple8 slice, whose length is max of input collections. 323 | func Zip8[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7, collection8 []T8) []Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { 324 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7), len(collection8)) 325 | result := make([]Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], maxLength) 326 | for index := 0; index < maxLength; index++ { 327 | a, _ := Nth(collection1, index) 328 | b, _ := Nth(collection2, index) 329 | c, _ := Nth(collection3, index) 330 | d, _ := Nth(collection4, index) 331 | e, _ := Nth(collection5, index) 332 | f, _ := Nth(collection6, index) 333 | g, _ := Nth(collection7, index) 334 | h, _ := Nth(collection8, index) 335 | result[index] = Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h } 336 | } 337 | return result 338 | } 339 | 340 | // Unzip8 returns 8 slices, whose elements come from Tuple8-collection. 341 | func Unzip8[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any](collection []Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7, []T8) { 342 | length := len(collection) 343 | result1 := make([]T1, length) 344 | result2 := make([]T2, length) 345 | result3 := make([]T3, length) 346 | result4 := make([]T4, length) 347 | result5 := make([]T5, length) 348 | result6 := make([]T6, length) 349 | result7 := make([]T7, length) 350 | result8 := make([]T8, length) 351 | for index := 0; index < length; index++ { 352 | result1[index] = collection[index].A 353 | result2[index] = collection[index].B 354 | result3[index] = collection[index].C 355 | result4[index] = collection[index].D 356 | result5[index] = collection[index].E 357 | result6[index] = collection[index].F 358 | result7[index] = collection[index].G 359 | result8[index] = collection[index].H 360 | } 361 | return result1, result2, result3, result4, result5, result6, result7, result8 362 | } 363 | 364 | type Tuple9[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any] struct { 365 | A T1 366 | B T2 367 | C T3 368 | D T4 369 | E T5 370 | F T6 371 | G T7 372 | H T8 373 | I T9 374 | } 375 | 376 | // Pack9 returns a Tuple9 instance. 377 | func Pack9[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7, h T8, i T9) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { 378 | return Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i } 379 | } 380 | 381 | // Unpack9 returns a Tuple9's inner value. 382 | func Unpack9[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any](t9 Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) (T1, T2, T3, T4, T5, T6, T7, T8, T9) { 383 | return t9.A, t9.B, t9.C, t9.D, t9.E, t9.F, t9.G, t9.H, t9.I 384 | } 385 | 386 | // Zip9 returns a Tuple9 slice, whose length is max of input collections. 387 | func Zip9[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7, collection8 []T8, collection9 []T9) []Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { 388 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7), len(collection8), len(collection9)) 389 | result := make([]Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], maxLength) 390 | for index := 0; index < maxLength; index++ { 391 | a, _ := Nth(collection1, index) 392 | b, _ := Nth(collection2, index) 393 | c, _ := Nth(collection3, index) 394 | d, _ := Nth(collection4, index) 395 | e, _ := Nth(collection5, index) 396 | f, _ := Nth(collection6, index) 397 | g, _ := Nth(collection7, index) 398 | h, _ := Nth(collection8, index) 399 | i, _ := Nth(collection9, index) 400 | result[index] = Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i } 401 | } 402 | return result 403 | } 404 | 405 | // Unzip9 returns 9 slices, whose elements come from Tuple9-collection. 406 | func Unzip9[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any](collection []Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7, []T8, []T9) { 407 | length := len(collection) 408 | result1 := make([]T1, length) 409 | result2 := make([]T2, length) 410 | result3 := make([]T3, length) 411 | result4 := make([]T4, length) 412 | result5 := make([]T5, length) 413 | result6 := make([]T6, length) 414 | result7 := make([]T7, length) 415 | result8 := make([]T8, length) 416 | result9 := make([]T9, length) 417 | for index := 0; index < length; index++ { 418 | result1[index] = collection[index].A 419 | result2[index] = collection[index].B 420 | result3[index] = collection[index].C 421 | result4[index] = collection[index].D 422 | result5[index] = collection[index].E 423 | result6[index] = collection[index].F 424 | result7[index] = collection[index].G 425 | result8[index] = collection[index].H 426 | result9[index] = collection[index].I 427 | } 428 | return result1, result2, result3, result4, result5, result6, result7, result8, result9 429 | } 430 | 431 | type Tuple10[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any] struct { 432 | A T1 433 | B T2 434 | C T3 435 | D T4 436 | E T5 437 | F T6 438 | G T7 439 | H T8 440 | I T9 441 | J T10 442 | } 443 | 444 | // Pack10 returns a Tuple10 instance. 445 | func Pack10[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7, h T8, i T9, j T10) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { 446 | return Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j } 447 | } 448 | 449 | // Unpack10 returns a Tuple10's inner value. 450 | func Unpack10[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any](t10 Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) { 451 | return t10.A, t10.B, t10.C, t10.D, t10.E, t10.F, t10.G, t10.H, t10.I, t10.J 452 | } 453 | 454 | // Zip10 returns a Tuple10 slice, whose length is max of input collections. 455 | func Zip10[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7, collection8 []T8, collection9 []T9, collection10 []T10) []Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { 456 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7), len(collection8), len(collection9), len(collection10)) 457 | result := make([]Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], maxLength) 458 | for index := 0; index < maxLength; index++ { 459 | a, _ := Nth(collection1, index) 460 | b, _ := Nth(collection2, index) 461 | c, _ := Nth(collection3, index) 462 | d, _ := Nth(collection4, index) 463 | e, _ := Nth(collection5, index) 464 | f, _ := Nth(collection6, index) 465 | g, _ := Nth(collection7, index) 466 | h, _ := Nth(collection8, index) 467 | i, _ := Nth(collection9, index) 468 | j, _ := Nth(collection10, index) 469 | result[index] = Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j } 470 | } 471 | return result 472 | } 473 | 474 | // Unzip10 returns 10 slices, whose elements come from Tuple10-collection. 475 | func Unzip10[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any](collection []Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7, []T8, []T9, []T10) { 476 | length := len(collection) 477 | result1 := make([]T1, length) 478 | result2 := make([]T2, length) 479 | result3 := make([]T3, length) 480 | result4 := make([]T4, length) 481 | result5 := make([]T5, length) 482 | result6 := make([]T6, length) 483 | result7 := make([]T7, length) 484 | result8 := make([]T8, length) 485 | result9 := make([]T9, length) 486 | result10 := make([]T10, length) 487 | for index := 0; index < length; index++ { 488 | result1[index] = collection[index].A 489 | result2[index] = collection[index].B 490 | result3[index] = collection[index].C 491 | result4[index] = collection[index].D 492 | result5[index] = collection[index].E 493 | result6[index] = collection[index].F 494 | result7[index] = collection[index].G 495 | result8[index] = collection[index].H 496 | result9[index] = collection[index].I 497 | result10[index] = collection[index].J 498 | } 499 | return result1, result2, result3, result4, result5, result6, result7, result8, result9, result10 500 | } 501 | 502 | type Tuple11[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any] struct { 503 | A T1 504 | B T2 505 | C T3 506 | D T4 507 | E T5 508 | F T6 509 | G T7 510 | H T8 511 | I T9 512 | J T10 513 | K T11 514 | } 515 | 516 | // Pack11 returns a Tuple11 instance. 517 | func Pack11[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7, h T8, i T9, j T10, k T11) Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] { 518 | return Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k } 519 | } 520 | 521 | // Unpack11 returns a Tuple11's inner value. 522 | func Unpack11[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any](t11 Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) { 523 | return t11.A, t11.B, t11.C, t11.D, t11.E, t11.F, t11.G, t11.H, t11.I, t11.J, t11.K 524 | } 525 | 526 | // Zip11 returns a Tuple11 slice, whose length is max of input collections. 527 | func Zip11[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7, collection8 []T8, collection9 []T9, collection10 []T10, collection11 []T11) []Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] { 528 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7), len(collection8), len(collection9), len(collection10), len(collection11)) 529 | result := make([]Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], maxLength) 530 | for index := 0; index < maxLength; index++ { 531 | a, _ := Nth(collection1, index) 532 | b, _ := Nth(collection2, index) 533 | c, _ := Nth(collection3, index) 534 | d, _ := Nth(collection4, index) 535 | e, _ := Nth(collection5, index) 536 | f, _ := Nth(collection6, index) 537 | g, _ := Nth(collection7, index) 538 | h, _ := Nth(collection8, index) 539 | i, _ := Nth(collection9, index) 540 | j, _ := Nth(collection10, index) 541 | k, _ := Nth(collection11, index) 542 | result[index] = Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k } 543 | } 544 | return result 545 | } 546 | 547 | // Unzip11 returns 11 slices, whose elements come from Tuple11-collection. 548 | func Unzip11[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any](collection []Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7, []T8, []T9, []T10, []T11) { 549 | length := len(collection) 550 | result1 := make([]T1, length) 551 | result2 := make([]T2, length) 552 | result3 := make([]T3, length) 553 | result4 := make([]T4, length) 554 | result5 := make([]T5, length) 555 | result6 := make([]T6, length) 556 | result7 := make([]T7, length) 557 | result8 := make([]T8, length) 558 | result9 := make([]T9, length) 559 | result10 := make([]T10, length) 560 | result11 := make([]T11, length) 561 | for index := 0; index < length; index++ { 562 | result1[index] = collection[index].A 563 | result2[index] = collection[index].B 564 | result3[index] = collection[index].C 565 | result4[index] = collection[index].D 566 | result5[index] = collection[index].E 567 | result6[index] = collection[index].F 568 | result7[index] = collection[index].G 569 | result8[index] = collection[index].H 570 | result9[index] = collection[index].I 571 | result10[index] = collection[index].J 572 | result11[index] = collection[index].K 573 | } 574 | return result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11 575 | } 576 | 577 | type Tuple12[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any] struct { 578 | A T1 579 | B T2 580 | C T3 581 | D T4 582 | E T5 583 | F T6 584 | G T7 585 | H T8 586 | I T9 587 | J T10 588 | K T11 589 | L T12 590 | } 591 | 592 | // Pack12 returns a Tuple12 instance. 593 | func Pack12[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7, h T8, i T9, j T10, k T11, l T12) Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] { 594 | return Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l } 595 | } 596 | 597 | // Unpack12 returns a Tuple12's inner value. 598 | func Unpack12[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any](t12 Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) { 599 | return t12.A, t12.B, t12.C, t12.D, t12.E, t12.F, t12.G, t12.H, t12.I, t12.J, t12.K, t12.L 600 | } 601 | 602 | // Zip12 returns a Tuple12 slice, whose length is max of input collections. 603 | func Zip12[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7, collection8 []T8, collection9 []T9, collection10 []T10, collection11 []T11, collection12 []T12) []Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] { 604 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7), len(collection8), len(collection9), len(collection10), len(collection11), len(collection12)) 605 | result := make([]Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], maxLength) 606 | for index := 0; index < maxLength; index++ { 607 | a, _ := Nth(collection1, index) 608 | b, _ := Nth(collection2, index) 609 | c, _ := Nth(collection3, index) 610 | d, _ := Nth(collection4, index) 611 | e, _ := Nth(collection5, index) 612 | f, _ := Nth(collection6, index) 613 | g, _ := Nth(collection7, index) 614 | h, _ := Nth(collection8, index) 615 | i, _ := Nth(collection9, index) 616 | j, _ := Nth(collection10, index) 617 | k, _ := Nth(collection11, index) 618 | l, _ := Nth(collection12, index) 619 | result[index] = Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l } 620 | } 621 | return result 622 | } 623 | 624 | // Unzip12 returns 12 slices, whose elements come from Tuple12-collection. 625 | func Unzip12[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any](collection []Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7, []T8, []T9, []T10, []T11, []T12) { 626 | length := len(collection) 627 | result1 := make([]T1, length) 628 | result2 := make([]T2, length) 629 | result3 := make([]T3, length) 630 | result4 := make([]T4, length) 631 | result5 := make([]T5, length) 632 | result6 := make([]T6, length) 633 | result7 := make([]T7, length) 634 | result8 := make([]T8, length) 635 | result9 := make([]T9, length) 636 | result10 := make([]T10, length) 637 | result11 := make([]T11, length) 638 | result12 := make([]T12, length) 639 | for index := 0; index < length; index++ { 640 | result1[index] = collection[index].A 641 | result2[index] = collection[index].B 642 | result3[index] = collection[index].C 643 | result4[index] = collection[index].D 644 | result5[index] = collection[index].E 645 | result6[index] = collection[index].F 646 | result7[index] = collection[index].G 647 | result8[index] = collection[index].H 648 | result9[index] = collection[index].I 649 | result10[index] = collection[index].J 650 | result11[index] = collection[index].K 651 | result12[index] = collection[index].L 652 | } 653 | return result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11, result12 654 | } 655 | 656 | type Tuple13[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any] struct { 657 | A T1 658 | B T2 659 | C T3 660 | D T4 661 | E T5 662 | F T6 663 | G T7 664 | H T8 665 | I T9 666 | J T10 667 | K T11 668 | L T12 669 | M T13 670 | } 671 | 672 | // Pack13 returns a Tuple13 instance. 673 | func Pack13[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7, h T8, i T9, j T10, k T11, l T12, m T13) Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] { 674 | return Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m } 675 | } 676 | 677 | // Unpack13 returns a Tuple13's inner value. 678 | func Unpack13[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any](t13 Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) { 679 | return t13.A, t13.B, t13.C, t13.D, t13.E, t13.F, t13.G, t13.H, t13.I, t13.J, t13.K, t13.L, t13.M 680 | } 681 | 682 | // Zip13 returns a Tuple13 slice, whose length is max of input collections. 683 | func Zip13[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7, collection8 []T8, collection9 []T9, collection10 []T10, collection11 []T11, collection12 []T12, collection13 []T13) []Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] { 684 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7), len(collection8), len(collection9), len(collection10), len(collection11), len(collection12), len(collection13)) 685 | result := make([]Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], maxLength) 686 | for index := 0; index < maxLength; index++ { 687 | a, _ := Nth(collection1, index) 688 | b, _ := Nth(collection2, index) 689 | c, _ := Nth(collection3, index) 690 | d, _ := Nth(collection4, index) 691 | e, _ := Nth(collection5, index) 692 | f, _ := Nth(collection6, index) 693 | g, _ := Nth(collection7, index) 694 | h, _ := Nth(collection8, index) 695 | i, _ := Nth(collection9, index) 696 | j, _ := Nth(collection10, index) 697 | k, _ := Nth(collection11, index) 698 | l, _ := Nth(collection12, index) 699 | m, _ := Nth(collection13, index) 700 | result[index] = Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m } 701 | } 702 | return result 703 | } 704 | 705 | // Unzip13 returns 13 slices, whose elements come from Tuple13-collection. 706 | func Unzip13[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any](collection []Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7, []T8, []T9, []T10, []T11, []T12, []T13) { 707 | length := len(collection) 708 | result1 := make([]T1, length) 709 | result2 := make([]T2, length) 710 | result3 := make([]T3, length) 711 | result4 := make([]T4, length) 712 | result5 := make([]T5, length) 713 | result6 := make([]T6, length) 714 | result7 := make([]T7, length) 715 | result8 := make([]T8, length) 716 | result9 := make([]T9, length) 717 | result10 := make([]T10, length) 718 | result11 := make([]T11, length) 719 | result12 := make([]T12, length) 720 | result13 := make([]T13, length) 721 | for index := 0; index < length; index++ { 722 | result1[index] = collection[index].A 723 | result2[index] = collection[index].B 724 | result3[index] = collection[index].C 725 | result4[index] = collection[index].D 726 | result5[index] = collection[index].E 727 | result6[index] = collection[index].F 728 | result7[index] = collection[index].G 729 | result8[index] = collection[index].H 730 | result9[index] = collection[index].I 731 | result10[index] = collection[index].J 732 | result11[index] = collection[index].K 733 | result12[index] = collection[index].L 734 | result13[index] = collection[index].M 735 | } 736 | return result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11, result12, result13 737 | } 738 | 739 | type Tuple14[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any] struct { 740 | A T1 741 | B T2 742 | C T3 743 | D T4 744 | E T5 745 | F T6 746 | G T7 747 | H T8 748 | I T9 749 | J T10 750 | K T11 751 | L T12 752 | M T13 753 | N T14 754 | } 755 | 756 | // Pack14 returns a Tuple14 instance. 757 | func Pack14[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7, h T8, i T9, j T10, k T11, l T12, m T13, n T14) Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] { 758 | return Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m, N: n } 759 | } 760 | 761 | // Unpack14 returns a Tuple14's inner value. 762 | func Unpack14[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any](t14 Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) { 763 | return t14.A, t14.B, t14.C, t14.D, t14.E, t14.F, t14.G, t14.H, t14.I, t14.J, t14.K, t14.L, t14.M, t14.N 764 | } 765 | 766 | // Zip14 returns a Tuple14 slice, whose length is max of input collections. 767 | func Zip14[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7, collection8 []T8, collection9 []T9, collection10 []T10, collection11 []T11, collection12 []T12, collection13 []T13, collection14 []T14) []Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] { 768 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7), len(collection8), len(collection9), len(collection10), len(collection11), len(collection12), len(collection13), len(collection14)) 769 | result := make([]Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], maxLength) 770 | for index := 0; index < maxLength; index++ { 771 | a, _ := Nth(collection1, index) 772 | b, _ := Nth(collection2, index) 773 | c, _ := Nth(collection3, index) 774 | d, _ := Nth(collection4, index) 775 | e, _ := Nth(collection5, index) 776 | f, _ := Nth(collection6, index) 777 | g, _ := Nth(collection7, index) 778 | h, _ := Nth(collection8, index) 779 | i, _ := Nth(collection9, index) 780 | j, _ := Nth(collection10, index) 781 | k, _ := Nth(collection11, index) 782 | l, _ := Nth(collection12, index) 783 | m, _ := Nth(collection13, index) 784 | n, _ := Nth(collection14, index) 785 | result[index] = Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m, N: n } 786 | } 787 | return result 788 | } 789 | 790 | // Unzip14 returns 14 slices, whose elements come from Tuple14-collection. 791 | func Unzip14[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any](collection []Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7, []T8, []T9, []T10, []T11, []T12, []T13, []T14) { 792 | length := len(collection) 793 | result1 := make([]T1, length) 794 | result2 := make([]T2, length) 795 | result3 := make([]T3, length) 796 | result4 := make([]T4, length) 797 | result5 := make([]T5, length) 798 | result6 := make([]T6, length) 799 | result7 := make([]T7, length) 800 | result8 := make([]T8, length) 801 | result9 := make([]T9, length) 802 | result10 := make([]T10, length) 803 | result11 := make([]T11, length) 804 | result12 := make([]T12, length) 805 | result13 := make([]T13, length) 806 | result14 := make([]T14, length) 807 | for index := 0; index < length; index++ { 808 | result1[index] = collection[index].A 809 | result2[index] = collection[index].B 810 | result3[index] = collection[index].C 811 | result4[index] = collection[index].D 812 | result5[index] = collection[index].E 813 | result6[index] = collection[index].F 814 | result7[index] = collection[index].G 815 | result8[index] = collection[index].H 816 | result9[index] = collection[index].I 817 | result10[index] = collection[index].J 818 | result11[index] = collection[index].K 819 | result12[index] = collection[index].L 820 | result13[index] = collection[index].M 821 | result14[index] = collection[index].N 822 | } 823 | return result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11, result12, result13, result14 824 | } 825 | 826 | type Tuple15[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any] struct { 827 | A T1 828 | B T2 829 | C T3 830 | D T4 831 | E T5 832 | F T6 833 | G T7 834 | H T8 835 | I T9 836 | J T10 837 | K T11 838 | L T12 839 | M T13 840 | N T14 841 | O T15 842 | } 843 | 844 | // Pack15 returns a Tuple15 instance. 845 | func Pack15[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7, h T8, i T9, j T10, k T11, l T12, m T13, n T14, o T15) Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] { 846 | return Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m, N: n, O: o } 847 | } 848 | 849 | // Unpack15 returns a Tuple15's inner value. 850 | func Unpack15[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any](t15 Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) { 851 | return t15.A, t15.B, t15.C, t15.D, t15.E, t15.F, t15.G, t15.H, t15.I, t15.J, t15.K, t15.L, t15.M, t15.N, t15.O 852 | } 853 | 854 | // Zip15 returns a Tuple15 slice, whose length is max of input collections. 855 | func Zip15[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7, collection8 []T8, collection9 []T9, collection10 []T10, collection11 []T11, collection12 []T12, collection13 []T13, collection14 []T14, collection15 []T15) []Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] { 856 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7), len(collection8), len(collection9), len(collection10), len(collection11), len(collection12), len(collection13), len(collection14), len(collection15)) 857 | result := make([]Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], maxLength) 858 | for index := 0; index < maxLength; index++ { 859 | a, _ := Nth(collection1, index) 860 | b, _ := Nth(collection2, index) 861 | c, _ := Nth(collection3, index) 862 | d, _ := Nth(collection4, index) 863 | e, _ := Nth(collection5, index) 864 | f, _ := Nth(collection6, index) 865 | g, _ := Nth(collection7, index) 866 | h, _ := Nth(collection8, index) 867 | i, _ := Nth(collection9, index) 868 | j, _ := Nth(collection10, index) 869 | k, _ := Nth(collection11, index) 870 | l, _ := Nth(collection12, index) 871 | m, _ := Nth(collection13, index) 872 | n, _ := Nth(collection14, index) 873 | o, _ := Nth(collection15, index) 874 | result[index] = Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m, N: n, O: o } 875 | } 876 | return result 877 | } 878 | 879 | // Unzip15 returns 15 slices, whose elements come from Tuple15-collection. 880 | func Unzip15[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any](collection []Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7, []T8, []T9, []T10, []T11, []T12, []T13, []T14, []T15) { 881 | length := len(collection) 882 | result1 := make([]T1, length) 883 | result2 := make([]T2, length) 884 | result3 := make([]T3, length) 885 | result4 := make([]T4, length) 886 | result5 := make([]T5, length) 887 | result6 := make([]T6, length) 888 | result7 := make([]T7, length) 889 | result8 := make([]T8, length) 890 | result9 := make([]T9, length) 891 | result10 := make([]T10, length) 892 | result11 := make([]T11, length) 893 | result12 := make([]T12, length) 894 | result13 := make([]T13, length) 895 | result14 := make([]T14, length) 896 | result15 := make([]T15, length) 897 | for index := 0; index < length; index++ { 898 | result1[index] = collection[index].A 899 | result2[index] = collection[index].B 900 | result3[index] = collection[index].C 901 | result4[index] = collection[index].D 902 | result5[index] = collection[index].E 903 | result6[index] = collection[index].F 904 | result7[index] = collection[index].G 905 | result8[index] = collection[index].H 906 | result9[index] = collection[index].I 907 | result10[index] = collection[index].J 908 | result11[index] = collection[index].K 909 | result12[index] = collection[index].L 910 | result13[index] = collection[index].M 911 | result14[index] = collection[index].N 912 | result15[index] = collection[index].O 913 | } 914 | return result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11, result12, result13, result14, result15 915 | } 916 | 917 | type Tuple16[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any, T16 any] struct { 918 | A T1 919 | B T2 920 | C T3 921 | D T4 922 | E T5 923 | F T6 924 | G T7 925 | H T8 926 | I T9 927 | J T10 928 | K T11 929 | L T12 930 | M T13 931 | N T14 932 | O T15 933 | P T16 934 | } 935 | 936 | // Pack16 returns a Tuple16 instance. 937 | func Pack16[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any, T16 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7, h T8, i T9, j T10, k T11, l T12, m T13, n T14, o T15, p T16) Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16] { 938 | return Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m, N: n, O: o, P: p } 939 | } 940 | 941 | // Unpack16 returns a Tuple16's inner value. 942 | func Unpack16[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any, T16 any](t16 Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]) (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) { 943 | return t16.A, t16.B, t16.C, t16.D, t16.E, t16.F, t16.G, t16.H, t16.I, t16.J, t16.K, t16.L, t16.M, t16.N, t16.O, t16.P 944 | } 945 | 946 | // Zip16 returns a Tuple16 slice, whose length is max of input collections. 947 | func Zip16[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any, T16 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7, collection8 []T8, collection9 []T9, collection10 []T10, collection11 []T11, collection12 []T12, collection13 []T13, collection14 []T14, collection15 []T15, collection16 []T16) []Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16] { 948 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7), len(collection8), len(collection9), len(collection10), len(collection11), len(collection12), len(collection13), len(collection14), len(collection15), len(collection16)) 949 | result := make([]Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16], maxLength) 950 | for index := 0; index < maxLength; index++ { 951 | a, _ := Nth(collection1, index) 952 | b, _ := Nth(collection2, index) 953 | c, _ := Nth(collection3, index) 954 | d, _ := Nth(collection4, index) 955 | e, _ := Nth(collection5, index) 956 | f, _ := Nth(collection6, index) 957 | g, _ := Nth(collection7, index) 958 | h, _ := Nth(collection8, index) 959 | i, _ := Nth(collection9, index) 960 | j, _ := Nth(collection10, index) 961 | k, _ := Nth(collection11, index) 962 | l, _ := Nth(collection12, index) 963 | m, _ := Nth(collection13, index) 964 | n, _ := Nth(collection14, index) 965 | o, _ := Nth(collection15, index) 966 | p, _ := Nth(collection16, index) 967 | result[index] = Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m, N: n, O: o, P: p } 968 | } 969 | return result 970 | } 971 | 972 | // Unzip16 returns 16 slices, whose elements come from Tuple16-collection. 973 | func Unzip16[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any, T16 any](collection []Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7, []T8, []T9, []T10, []T11, []T12, []T13, []T14, []T15, []T16) { 974 | length := len(collection) 975 | result1 := make([]T1, length) 976 | result2 := make([]T2, length) 977 | result3 := make([]T3, length) 978 | result4 := make([]T4, length) 979 | result5 := make([]T5, length) 980 | result6 := make([]T6, length) 981 | result7 := make([]T7, length) 982 | result8 := make([]T8, length) 983 | result9 := make([]T9, length) 984 | result10 := make([]T10, length) 985 | result11 := make([]T11, length) 986 | result12 := make([]T12, length) 987 | result13 := make([]T13, length) 988 | result14 := make([]T14, length) 989 | result15 := make([]T15, length) 990 | result16 := make([]T16, length) 991 | for index := 0; index < length; index++ { 992 | result1[index] = collection[index].A 993 | result2[index] = collection[index].B 994 | result3[index] = collection[index].C 995 | result4[index] = collection[index].D 996 | result5[index] = collection[index].E 997 | result6[index] = collection[index].F 998 | result7[index] = collection[index].G 999 | result8[index] = collection[index].H 1000 | result9[index] = collection[index].I 1001 | result10[index] = collection[index].J 1002 | result11[index] = collection[index].K 1003 | result12[index] = collection[index].L 1004 | result13[index] = collection[index].M 1005 | result14[index] = collection[index].N 1006 | result15[index] = collection[index].O 1007 | result16[index] = collection[index].P 1008 | } 1009 | return result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11, result12, result13, result14, result15, result16 1010 | } 1011 | 1012 | type Tuple17[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any, T16 any, T17 any] struct { 1013 | A T1 1014 | B T2 1015 | C T3 1016 | D T4 1017 | E T5 1018 | F T6 1019 | G T7 1020 | H T8 1021 | I T9 1022 | J T10 1023 | K T11 1024 | L T12 1025 | M T13 1026 | N T14 1027 | O T15 1028 | P T16 1029 | Q T17 1030 | } 1031 | 1032 | // Pack17 returns a Tuple17 instance. 1033 | func Pack17[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any, T16 any, T17 any](a T1, b T2, c T3, d T4, e T5, f T6, g T7, h T8, i T9, j T10, k T11, l T12, m T13, n T14, o T15, p T16, q T17) Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17] { 1034 | return Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m, N: n, O: o, P: p, Q: q } 1035 | } 1036 | 1037 | // Unpack17 returns a Tuple17's inner value. 1038 | func Unpack17[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any, T16 any, T17 any](t17 Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]) (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) { 1039 | return t17.A, t17.B, t17.C, t17.D, t17.E, t17.F, t17.G, t17.H, t17.I, t17.J, t17.K, t17.L, t17.M, t17.N, t17.O, t17.P, t17.Q 1040 | } 1041 | 1042 | // Zip17 returns a Tuple17 slice, whose length is max of input collections. 1043 | func Zip17[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any, T16 any, T17 any](collection1 []T1, collection2 []T2, collection3 []T3, collection4 []T4, collection5 []T5, collection6 []T6, collection7 []T7, collection8 []T8, collection9 []T9, collection10 []T10, collection11 []T11, collection12 []T12, collection13 []T13, collection14 []T14, collection15 []T15, collection16 []T16, collection17 []T17) []Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17] { 1044 | maxLength := Max(len(collection1), len(collection2), len(collection3), len(collection4), len(collection5), len(collection6), len(collection7), len(collection8), len(collection9), len(collection10), len(collection11), len(collection12), len(collection13), len(collection14), len(collection15), len(collection16), len(collection17)) 1045 | result := make([]Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17], maxLength) 1046 | for index := 0; index < maxLength; index++ { 1047 | a, _ := Nth(collection1, index) 1048 | b, _ := Nth(collection2, index) 1049 | c, _ := Nth(collection3, index) 1050 | d, _ := Nth(collection4, index) 1051 | e, _ := Nth(collection5, index) 1052 | f, _ := Nth(collection6, index) 1053 | g, _ := Nth(collection7, index) 1054 | h, _ := Nth(collection8, index) 1055 | i, _ := Nth(collection9, index) 1056 | j, _ := Nth(collection10, index) 1057 | k, _ := Nth(collection11, index) 1058 | l, _ := Nth(collection12, index) 1059 | m, _ := Nth(collection13, index) 1060 | n, _ := Nth(collection14, index) 1061 | o, _ := Nth(collection15, index) 1062 | p, _ := Nth(collection16, index) 1063 | q, _ := Nth(collection17, index) 1064 | result[index] = Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]{ A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m, N: n, O: o, P: p, Q: q } 1065 | } 1066 | return result 1067 | } 1068 | 1069 | // Unzip17 returns 17 slices, whose elements come from Tuple17-collection. 1070 | func Unzip17[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any, T10 any, T11 any, T12 any, T13 any, T14 any, T15 any, T16 any, T17 any](collection []Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]) ([]T1, []T2, []T3, []T4, []T5, []T6, []T7, []T8, []T9, []T10, []T11, []T12, []T13, []T14, []T15, []T16, []T17) { 1071 | length := len(collection) 1072 | result1 := make([]T1, length) 1073 | result2 := make([]T2, length) 1074 | result3 := make([]T3, length) 1075 | result4 := make([]T4, length) 1076 | result5 := make([]T5, length) 1077 | result6 := make([]T6, length) 1078 | result7 := make([]T7, length) 1079 | result8 := make([]T8, length) 1080 | result9 := make([]T9, length) 1081 | result10 := make([]T10, length) 1082 | result11 := make([]T11, length) 1083 | result12 := make([]T12, length) 1084 | result13 := make([]T13, length) 1085 | result14 := make([]T14, length) 1086 | result15 := make([]T15, length) 1087 | result16 := make([]T16, length) 1088 | result17 := make([]T17, length) 1089 | for index := 0; index < length; index++ { 1090 | result1[index] = collection[index].A 1091 | result2[index] = collection[index].B 1092 | result3[index] = collection[index].C 1093 | result4[index] = collection[index].D 1094 | result5[index] = collection[index].E 1095 | result6[index] = collection[index].F 1096 | result7[index] = collection[index].G 1097 | result8[index] = collection[index].H 1098 | result9[index] = collection[index].I 1099 | result10[index] = collection[index].J 1100 | result11[index] = collection[index].K 1101 | result12[index] = collection[index].L 1102 | result13[index] = collection[index].M 1103 | result14[index] = collection[index].N 1104 | result15[index] = collection[index].O 1105 | result16[index] = collection[index].P 1106 | result17[index] = collection[index].Q 1107 | } 1108 | return result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11, result12, result13, result14, result15, result16, result17 1109 | } 1110 | 1111 | -------------------------------------------------------------------------------- /tuples_example_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import "fmt" 4 | 5 | func ExamplePack2() { 6 | result := Pack2(7, "beautiful number") 7 | fmt.Printf("%+v", result) 8 | // Output: 9 | // {A:7 B:beautiful number} 10 | } 11 | 12 | func ExamplePack3() { 13 | result := Pack3(7, "beautiful number", true) 14 | fmt.Printf("%+v", result) 15 | // Output: 16 | // {A:7 B:beautiful number C:true} 17 | } 18 | 19 | func ExamplePack4() { 20 | result := Pack4(7, "beautiful number", true, 3.14) 21 | fmt.Printf("%+v", result) 22 | // Output: 23 | // {A:7 B:beautiful number C:true D:3.14} 24 | } 25 | 26 | func ExamplePack5() { 27 | result := Pack5(7, "beautiful number", true, 3.14, "pi") 28 | fmt.Printf("%+v", result) 29 | // Output: 30 | // {A:7 B:beautiful number C:true D:3.14 E:pi} 31 | } 32 | 33 | func ExamplePack6() { 34 | result := Pack6(7, "beautiful number", true, 3.14, "pi", 3.14) 35 | fmt.Printf("%+v", result) 36 | // Output: 37 | // {A:7 B:beautiful number C:true D:3.14 E:pi F:3.14} 38 | } 39 | 40 | func ExamplePack7() { 41 | result := Pack7(7, "beautiful number", true, 3.14, "pi", 3.14, "pi") 42 | fmt.Printf("%+v", result) 43 | // Output: 44 | // {A:7 B:beautiful number C:true D:3.14 E:pi F:3.14 G:pi} 45 | } 46 | 47 | func ExamplePack8() { 48 | result := Pack8(7, "beautiful number", true, 3.14, "pi", 3.14, "pi", 3.14) 49 | fmt.Printf("%+v", result) 50 | // Output: 51 | // {A:7 B:beautiful number C:true D:3.14 E:pi F:3.14 G:pi H:3.14} 52 | } 53 | 54 | func ExamplePack9() { 55 | result := Pack9(7, "beautiful number", true, 3.14, "pi", 3.14, "pi", 3.14, "pi") 56 | fmt.Printf("%+v", result) 57 | // Output: 58 | // {A:7 B:beautiful number C:true D:3.14 E:pi F:3.14 G:pi H:3.14 I:pi} 59 | } 60 | 61 | func ExamplePack10() { 62 | result := Pack10(7, "beautiful number", true, 3.14, "pi", 3.14, "pi", 3.14, "pi", 3.14) 63 | fmt.Printf("%+v", result) 64 | // Output: 65 | // {A:7 B:beautiful number C:true D:3.14 E:pi F:3.14 G:pi H:3.14 I:pi J:3.14} 66 | } 67 | 68 | func ExamplePack11() { 69 | result := Pack11(7, "beautiful number", true, 3.14, "pi", 3.14, "pi", 3.14, "pi", 3.14, "pi") 70 | fmt.Printf("%+v", result) 71 | // Output: 72 | // {A:7 B:beautiful number C:true D:3.14 E:pi F:3.14 G:pi H:3.14 I:pi J:3.14 K:pi} 73 | } 74 | 75 | func ExampleUnpack2() { 76 | result1, result2 := Unpack2(Tuple2[int, string]{A: 7, B: "b"}) 77 | fmt.Println(result1, result2) 78 | // Output: 79 | // 7 b 80 | } 81 | 82 | func ExampleUnpack3() { 83 | result1, result2, result3 := Unpack3(Tuple3[int, string, bool]{A: 7, B: "b", C: true}) 84 | fmt.Println(result1, result2, result3) 85 | // Output: 86 | // 7 b true 87 | } 88 | 89 | func ExampleUnpack4() { 90 | result1, result2, result3, result4 := Unpack4(Tuple4[int, string, bool, float64]{A: 7, B: "b", C: true, D: 3.14}) 91 | fmt.Println(result1, result2, result3, result4) 92 | // Output: 93 | // 7 b true 3.14 94 | } 95 | 96 | func ExampleUnpack5() { 97 | result1, result2, result3, result4, result5 := Unpack5(Tuple5[int, string, bool, float64, string]{A: 7, B: "b", C: true, D: 3.14, E: "e"}) 98 | fmt.Println(result1, result2, result3, result4, result5) 99 | // Output: 100 | // 7 b true 3.14 e 101 | } 102 | 103 | func ExampleUnpack6() { 104 | result1, result2, result3, result4, result5, result6 := Unpack6(Tuple6[int, string, bool, float64, string, float64]{A: 7, B: "b", C: true, D: 3.14, E: "e", F: 3.14}) 105 | fmt.Println(result1, result2, result3, result4, result5, result6) 106 | // Output: 107 | // 7 b true 3.14 e 3.14 108 | } 109 | 110 | func ExampleUnpack7() { 111 | result1, result2, result3, result4, result5, result6, result7 := Unpack7(Tuple7[int, string, bool, float64, string, float64, string]{A: 7, B: "b", C: true, D: 3.14, E: "e", F: 3.14, G: "g"}) 112 | fmt.Println(result1, result2, result3, result4, result5, result6, result7) 113 | // Output: 114 | // 7 b true 3.14 e 3.14 g 115 | } 116 | 117 | func ExampleUnpack8() { 118 | result1, result2, result3, result4, result5, result6, result7, result8 := Unpack8(Tuple8[int, string, bool, float64, string, float64, string, float64]{A: 7, B: "b", C: true, D: 3.14, E: "e", F: 3.14, G: "g", H: 3.14}) 119 | fmt.Println(result1, result2, result3, result4, result5, result6, result7, result8) 120 | // Output: 121 | // 7 b true 3.14 e 3.14 g 3.14 122 | } 123 | 124 | func ExampleUnpack9() { 125 | result1, result2, result3, result4, result5, result6, result7, result8, result9 := Unpack9(Tuple9[int, string, bool, float64, string, float64, string, float64, string]{A: 7, B: "b", C: true, D: 3.14, E: "e", F: 3.14, G: "g", H: 3.14, I: "i"}) 126 | fmt.Println(result1, result2, result3, result4, result5, result6, result7, result8, result9) 127 | // Output: 128 | // 7 b true 3.14 e 3.14 g 3.14 i 129 | } 130 | 131 | func ExampleUnpack10() { 132 | result1, result2, result3, result4, result5, result6, result7, result8, result9, result10 := Unpack10(Tuple10[int, string, bool, float64, string, float64, string, float64, string, float64]{A: 7, B: "b", C: true, D: 3.14, E: "e", F: 3.14, G: "g", H: 3.14, I: "i", J: 3.14}) 133 | fmt.Println(result1, result2, result3, result4, result5, result6, result7, result8, result9, result10) 134 | // Output: 135 | // 7 b true 3.14 e 3.14 g 3.14 i 3.14 136 | } 137 | 138 | func ExampleUnpack11() { 139 | result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11 := Unpack11(Tuple11[int, string, bool, float64, string, float64, string, float64, string, float64, string]{A: 7, B: "b", C: true, D: 3.14, E: "e", F: 3.14, G: "g", H: 3.14, I: "i", J: 3.14, K: "k"}) 140 | fmt.Println(result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11) 141 | // Output: 142 | // 7 b true 3.14 e 3.14 g 3.14 i 3.14 k 143 | } 144 | 145 | func ExampleZip2() { 146 | collection1 := []int{1, 2, 3, 4} 147 | collection2 := []string{"a", "b", "c"} 148 | result := Zip2(collection1, collection2) 149 | fmt.Println(result) 150 | // Output: 151 | // [{1 a} {2 b} {3 c} {4 }] 152 | } 153 | 154 | func ExampleZip3() { 155 | collection1 := []int{1, 2, 3, 4} 156 | collection2 := []string{"a", "b", "c"} 157 | collection3 := []bool{true, false} 158 | result := Zip3(collection1, collection2, collection3) 159 | fmt.Println(result) 160 | // Output: 161 | // [{1 a true} {2 b false} {3 c false} {4 false}] 162 | } 163 | 164 | func ExampleZip4() { 165 | collection1 := []int{1, 2, 3, 4} 166 | collection2 := []string{"a", "b", "c"} 167 | collection3 := []bool{true, false} 168 | collection4 := []float64{3.14, 2.72} 169 | result := Zip4(collection1, collection2, collection3, collection4) 170 | fmt.Println(result) 171 | // Output: 172 | // [{1 a true 3.14} {2 b false 2.72} {3 c false 0} {4 false 0}] 173 | } 174 | 175 | func ExampleZip5() { 176 | collection1 := []int{1, 2, 3, 4} 177 | collection2 := []string{"a", "b", "c"} 178 | collection3 := []bool{true, false} 179 | collection4 := []float64{3.14, 2.72} 180 | collection5 := []string{"e", "f"} 181 | result := Zip5(collection1, collection2, collection3, collection4, collection5) 182 | fmt.Println(result) 183 | // Output: 184 | // [{1 a true 3.14 e} {2 b false 2.72 f} {3 c false 0 } {4 false 0 }] 185 | } 186 | 187 | func ExampleZip6() { 188 | collection1 := []int{1, 2, 3, 4} 189 | collection2 := []string{"a", "b", "c"} 190 | collection3 := []bool{true, false} 191 | collection4 := []float64{3.14, 2.72} 192 | collection5 := []string{"e", "f"} 193 | collection6 := []float64{3.14} 194 | result := Zip6(collection1, collection2, collection3, collection4, collection5, collection6) 195 | fmt.Println(result) 196 | // Output: 197 | // [{1 a true 3.14 e 3.14} {2 b false 2.72 f 0} {3 c false 0 0} {4 false 0 0}] 198 | } 199 | 200 | func ExampleZip7() { 201 | collection1 := []int{1, 2, 3, 4} 202 | collection2 := []string{"a", "b", "c"} 203 | collection3 := []bool{true, false} 204 | collection4 := []float64{3.14, 2.72} 205 | collection5 := []string{"e", "f"} 206 | collection6 := []float64{3.14} 207 | collection7 := []string{"g"} 208 | result := Zip7(collection1, collection2, collection3, collection4, collection5, collection6, collection7) 209 | fmt.Println(result) 210 | // Output: 211 | // [{1 a true 3.14 e 3.14 g} {2 b false 2.72 f 0 } {3 c false 0 0 } {4 false 0 0 }] 212 | } 213 | 214 | func ExampleZip8() { 215 | collection1 := []int{1, 2, 3, 4} 216 | collection2 := []string{"a", "b", "c"} 217 | collection3 := []bool{true, false} 218 | collection4 := []float64{3.14, 2.72} 219 | collection5 := []string{"e", "f"} 220 | collection6 := []float64{3.14} 221 | collection7 := []string{"g"} 222 | collection8 := []float64{3.14} 223 | result := Zip8(collection1, collection2, collection3, collection4, collection5, collection6, collection7, collection8) 224 | fmt.Println(result) 225 | // Output: 226 | // [{1 a true 3.14 e 3.14 g 3.14} {2 b false 2.72 f 0 0} {3 c false 0 0 0} {4 false 0 0 0}] 227 | } 228 | 229 | func ExampleZip9() { 230 | collection1 := []int{1, 2, 3, 4} 231 | collection2 := []string{"a", "b", "c"} 232 | collection3 := []bool{true, false} 233 | collection4 := []float64{3.14, 2.72} 234 | collection5 := []string{"e", "f"} 235 | collection6 := []float64{3.14} 236 | collection7 := []string{"g"} 237 | collection8 := []float64{3.14} 238 | collection9 := []string{"i"} 239 | result := Zip9(collection1, collection2, collection3, collection4, collection5, collection6, collection7, collection8, collection9) 240 | fmt.Println(result) 241 | // Output: 242 | // [{1 a true 3.14 e 3.14 g 3.14 i} {2 b false 2.72 f 0 0 } {3 c false 0 0 0 } {4 false 0 0 0 }] 243 | } 244 | 245 | func ExampleZip10() { 246 | collection1 := []int{1, 2, 3, 4} 247 | collection2 := []string{"a", "b", "c"} 248 | collection3 := []bool{true, false} 249 | collection4 := []float64{3.14, 2.72} 250 | collection5 := []string{"e", "f"} 251 | collection6 := []float64{3.14} 252 | collection7 := []string{"g"} 253 | collection8 := []float64{3.14} 254 | collection9 := []string{"i"} 255 | collection10 := []float64{3.14} 256 | result := Zip10(collection1, collection2, collection3, collection4, collection5, collection6, collection7, collection8, collection9, collection10) 257 | fmt.Println(result) 258 | // Output: 259 | // [{1 a true 3.14 e 3.14 g 3.14 i 3.14} {2 b false 2.72 f 0 0 0} {3 c false 0 0 0 0} {4 false 0 0 0 0}] 260 | } 261 | 262 | func ExampleZip11() { 263 | collection1 := []int{1, 2, 3, 4} 264 | collection2 := []string{"a", "b", "c"} 265 | collection3 := []bool{true, false} 266 | collection4 := []float64{3.14, 2.72} 267 | collection5 := []string{"e", "f"} 268 | collection6 := []float64{3.14} 269 | collection7 := []string{"g"} 270 | collection8 := []float64{3.14} 271 | collection9 := []string{"i"} 272 | collection10 := []float64{3.14} 273 | collection11 := []string{"j"} 274 | result := Zip11(collection1, collection2, collection3, collection4, collection5, collection6, collection7, collection8, collection9, collection10, collection11) 275 | fmt.Println(result) 276 | // Output: 277 | // [{1 a true 3.14 e 3.14 g 3.14 i 3.14 j} {2 b false 2.72 f 0 0 0 } {3 c false 0 0 0 0 } {4 false 0 0 0 0 }] 278 | } 279 | 280 | func ExampleUnzip2() { 281 | collection := []Tuple2[int, string]{ 282 | {1, "a"}, 283 | {2, "b"}, 284 | {3, "c"}, 285 | } 286 | result1, result2 := Unzip2(collection) 287 | fmt.Println(result1, result2) 288 | // Output: 289 | // [1 2 3] [a b c] 290 | } 291 | 292 | func ExampleUnzip3() { 293 | collection := []Tuple3[int, string, bool]{ 294 | {1, "a", true}, 295 | {2, "b", false}, 296 | {3, "c", false}, 297 | } 298 | result1, result2, result3 := Unzip3(collection) 299 | fmt.Println(result1, result2, result3) 300 | // Output: 301 | // [1 2 3] [a b c] [true false false] 302 | } 303 | 304 | func ExampleUnzip4() { 305 | collection := []Tuple4[int, string, bool, float64]{ 306 | {1, "a", true, 3.14}, 307 | {2, "b", false, 2.72}, 308 | {3, "c", false, 0}, 309 | } 310 | result1, result2, result3, result4 := Unzip4(collection) 311 | fmt.Println(result1, result2, result3, result4) 312 | // Output: 313 | // [1 2 3] [a b c] [true false false] [3.14 2.72 0] 314 | } 315 | 316 | func ExampleUnzip5() { 317 | collection := []Tuple5[int, string, bool, float64, string]{ 318 | {1, "a", true, 3.14, "e"}, 319 | {2, "b", false, 2.72, "f"}, 320 | {3, "c", false, 0, ""}, 321 | } 322 | result1, result2, result3, result4, result5 := Unzip5(collection) 323 | fmt.Println(result1, result2, result3, result4, result5) 324 | // Output: 325 | // [1 2 3] [a b c] [true false false] [3.14 2.72 0] [e f ] 326 | } 327 | 328 | func ExampleUnzip6() { 329 | collection := []Tuple6[int, string, bool, float64, string, float64]{ 330 | {1, "a", true, 3.14, "e", 3.14}, 331 | {2, "b", false, 2.72, "f", 0}, 332 | {3, "c", false, 0, "", 0}, 333 | } 334 | result1, result2, result3, result4, result5, result6 := Unzip6(collection) 335 | fmt.Println(result1, result2, result3, result4, result5, result6) 336 | // Output: 337 | // [1 2 3] [a b c] [true false false] [3.14 2.72 0] [e f ] [3.14 0 0] 338 | } 339 | 340 | func ExampleUnzip7() { 341 | collection := []Tuple7[int, string, bool, float64, string, float64, string]{ 342 | {1, "a", true, 3.14, "e", 3.14, "g"}, 343 | {2, "b", false, 2.72, "f", 0, ""}, 344 | {3, "c", false, 0, "", 0, ""}, 345 | } 346 | result1, result2, result3, result4, result5, result6, result7 := Unzip7(collection) 347 | fmt.Println(result1, result2, result3, result4, result5, result6, result7) 348 | // Output: 349 | // [1 2 3] [a b c] [true false false] [3.14 2.72 0] [e f ] [3.14 0 0] [g ] 350 | } 351 | 352 | func ExampleUnzip8() { 353 | collection := []Tuple8[int, string, bool, float64, string, float64, string, float64]{ 354 | {1, "a", true, 3.14, "e", 3.14, "g", 3.14}, 355 | {2, "b", false, 2.72, "f", 0, "", 0}, 356 | {3, "c", false, 0, "", 0, "", 0}, 357 | } 358 | result1, result2, result3, result4, result5, result6, result7, result8 := Unzip8(collection) 359 | fmt.Println(result1, result2, result3, result4, result5, result6, result7, result8) 360 | // Output: 361 | // [1 2 3] [a b c] [true false false] [3.14 2.72 0] [e f ] [3.14 0 0] [g ] [3.14 0 0] 362 | } 363 | 364 | func ExampleUnzip9() { 365 | collection := []Tuple9[int, string, bool, float64, string, float64, string, float64, string]{ 366 | {1, "a", true, 3.14, "e", 3.14, "g", 3.14, "i"}, 367 | {2, "b", false, 2.72, "f", 0, "", 0, ""}, 368 | {3, "c", false, 0, "", 0, "", 0, ""}, 369 | } 370 | result1, result2, result3, result4, result5, result6, result7, result8, result9 := Unzip9(collection) 371 | fmt.Println(result1, result2, result3, result4, result5, result6, result7, result8, result9) 372 | // Output: 373 | // [1 2 3] [a b c] [true false false] [3.14 2.72 0] [e f ] [3.14 0 0] [g ] [3.14 0 0] [i ] 374 | } 375 | 376 | func ExampleUnzip10() { 377 | collection := []Tuple10[int, string, bool, float64, string, float64, string, float64, string, float64]{ 378 | {1, "a", true, 3.14, "e", 3.14, "g", 3.14, "i", 3.14}, 379 | {2, "b", false, 2.72, "f", 0, "", 0, "", 0}, 380 | {3, "c", false, 0, "", 0, "", 0, "", 0}, 381 | } 382 | result1, result2, result3, result4, result5, result6, result7, result8, result9, result10 := Unzip10(collection) 383 | fmt.Println(result1, result2, result3, result4, result5, result6, result7, result8, result9, result10) 384 | // Output: 385 | // [1 2 3] [a b c] [true false false] [3.14 2.72 0] [e f ] [3.14 0 0] [g ] [3.14 0 0] [i ] [3.14 0 0] 386 | } 387 | 388 | func ExampleUnzip11() { 389 | collection := []Tuple11[int, string, bool, float64, string, float64, string, float64, string, float64, string]{ 390 | {1, "a", true, 3.14, "e", 3.14, "g", 3.14, "i", 3.14, "k"}, 391 | {2, "b", false, 2.72, "f", 0, "", 0, "", 0, ""}, 392 | {3, "c", false, 0, "", 0, "", 0, "", 0, ""}, 393 | } 394 | result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11 := Unzip11(collection) 395 | fmt.Println(result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11) 396 | // Output: 397 | // [1 2 3] [a b c] [true false false] [3.14 2.72 0] [e f ] [3.14 0 0] [g ] [3.14 0 0] [i ] [3.14 0 0] [k ] 398 | } 399 | -------------------------------------------------------------------------------- /type_manipulation.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | // ToPtr returns a pointer copy of value. 4 | func ToPtr[T any](x T) *T { 5 | return &x 6 | } 7 | 8 | // FromPtr returns the pointer value, or zero if pointer is nil. 9 | func FromPtr[T any](x *T) T { 10 | if x == nil { 11 | return Zero[T]() 12 | } 13 | return *x 14 | } 15 | 16 | // FromPtrWithFallback returns the pointer value, or fallback if pointer is nil. 17 | func FromPtrWithFallback[T any](x *T, fallback T) T { 18 | if x == nil { 19 | return fallback 20 | } 21 | return *x 22 | } 23 | 24 | // ToSlicePtr returns a slice of elements that are pointer copies of collection. 25 | func ToSlicePtr[T any](collection []T) []*T { 26 | return Map[T, *T](collection, func(item T, index int) *T { 27 | return ToPtr(item) 28 | }) 29 | } 30 | 31 | // FromSlicePtr returns a slice of elements that are values of collection. 32 | func FromSlicePtr[T any](collection []*T) []T { 33 | return Map[*T, T](collection, func(item *T, index int) T { 34 | return FromPtr(item) 35 | }) 36 | } 37 | 38 | // FromSlicePtrWithFallback returns a slice of elements that are values with fallback of collection. 39 | func FromSlicePtrWithFallback[T any](collection []*T, fallback T) []T { 40 | return Map[*T, T](collection, func(item *T, index int) T { 41 | return FromPtrWithFallback(item, fallback) 42 | }) 43 | } 44 | 45 | // ToAnySlice returns a slice of elements whose types are any, from collection. 46 | func ToAnySlice[T any](collection []T) []any { 47 | return Map[T, any](collection, func(item T, index int) any { 48 | return item 49 | }) 50 | } 51 | 52 | // FromAnySlice returns a slice of elements whose types are T, from collection. 53 | func FromAnySlice[T any](collection []any) ([]T, bool) { 54 | ok := true 55 | result := Map[any, T](collection, func(item any, index int) T { 56 | if value, innerOk := item.(T); innerOk { 57 | return value 58 | } else { 59 | ok = false 60 | return Zero[T]() 61 | } 62 | }) 63 | return result, ok 64 | } 65 | 66 | // Zero returns zero value of T. 67 | func Zero[T any]() T { 68 | var zero T 69 | return zero 70 | } 71 | 72 | // IsZero returns true if x equals to zero value of T. 73 | func IsZero[T comparable](x T) bool { 74 | return x == Zero[T]() 75 | } 76 | 77 | // IsNotZero returns true if x not equals to zero value of T. 78 | func IsNotZero[T comparable](x T) bool { 79 | return !IsZero(x) 80 | } 81 | -------------------------------------------------------------------------------- /type_manipulation_example_test.go: -------------------------------------------------------------------------------- 1 | package toolbox 2 | 3 | import "fmt" 4 | 5 | func ExampleToPtr() { 6 | x := 1 7 | result := ToPtr(x) 8 | fmt.Println(*result) 9 | // Output: 10 | // 1 11 | } 12 | 13 | func ExampleFromPtr() { 14 | x := 1 15 | y := &x 16 | result := FromPtr(y) 17 | fmt.Println(result) 18 | // Output: 19 | // 1 20 | } 21 | 22 | func ExampleFromPtrWithFallback() { 23 | var x *int = nil 24 | result := FromPtrWithFallback(x, 1) 25 | fmt.Println(result) 26 | // Output: 27 | // 1 28 | } 29 | 30 | func ExampleToSlicePtr() { 31 | collection := []int{1, 2, 3, 4, 5} 32 | ToSlicePtr(collection) 33 | } 34 | 35 | func ExampleFromSlicePtr() { 36 | collection := []*int{ToPtr(1), ToPtr(2), ToPtr(3), ToPtr(4), ToPtr(5)} 37 | result := FromSlicePtr(collection) 38 | fmt.Println(result) 39 | // Output: 40 | // [1 2 3 4 5] 41 | } 42 | 43 | func ExampleFromSlicePtrWithFallback() { 44 | var nilValue *int = nil 45 | collection := []*int{ToPtr(1), ToPtr(2), nilValue, ToPtr(4), ToPtr(5)} 46 | result := FromSlicePtrWithFallback(collection, 7) 47 | fmt.Println(result) 48 | // Output: 49 | // [1 2 7 4 5] 50 | } 51 | 52 | func ExampleToAnySlice() { 53 | collection := []int{1, 2, 3, 4, 5} 54 | result := ToAnySlice(collection) 55 | fmt.Println(result) 56 | // Output: 57 | // [1 2 3 4 5] 58 | } 59 | 60 | func ExampleFromAnySlice() { 61 | collection := []any{1, 2, "3", 4, 5} 62 | result, ok := FromAnySlice[int](collection) 63 | fmt.Println(result, ok) 64 | // Output: 65 | // [1 2 0 4 5] false 66 | } 67 | 68 | func ExampleZero() { 69 | result := Zero[int]() 70 | fmt.Println(result) 71 | // Output: 72 | // 0 73 | } 74 | 75 | func ExampleIsZero() { 76 | result := IsZero[int](1) 77 | fmt.Println(result) 78 | // Output: 79 | // false 80 | } 81 | 82 | func ExampleIsNotZero() { 83 | result := IsNotZero[int](1) 84 | fmt.Println(result) 85 | // Output: 86 | // true 87 | } 88 | --------------------------------------------------------------------------------