├── .github ├── release.yml └── workflows │ ├── release.yml │ └── tests.yml ├── LICENSE ├── README.md ├── cache.go ├── cache_test.go ├── doc.go ├── go.mod ├── go.sum ├── iter └── iterators.go ├── iterator.go ├── iterator_test.go ├── linked_list.go ├── linked_list_test.go ├── map.go ├── set.go ├── vector.go └── vector_test.go /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: Breaking Changes 🛠 4 | labels: 5 | - Semver-Major 6 | - breaking-change 7 | - title: New Features 🎉 8 | labels: 9 | - Semver-Minor 10 | - enhancement 11 | - title: Crashed bugs 🐛 12 | labels: 13 | - bug 14 | - title: Documentation 📑 15 | labels: 16 | - documentation 17 | - title: Other Changes 18 | labels: 19 | - "*" 20 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' 5 | 6 | name: Create Release 7 | 8 | jobs: 9 | create-github-release: 10 | name: Create GitHub Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v3 15 | - name: Create Release 16 | run: gh release create ${{ github.ref }} --generate-notes 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | run-tests: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-go@v3 15 | with: 16 | go-version: '>=1.18.0' 17 | - run: go test 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ismail Gjevori 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Go Reference](https://pkg.go.dev/badge/github.com/isgj/collection.svg)](https://pkg.go.dev/github.com/isgj/collection) 2 | 3 | # collection 4 | 5 | Generic go structures 6 | 7 | ## Install 8 | 9 | ``` 10 | go get github.com/isgj/collection 11 | ``` 12 | 13 | ## Usage 14 | 15 | [collection.Vec[T]](https://pkg.go.dev/github.com/isgj/collection#Vec) implemented as a native go slice `[]T`. Because of this, other than the few currently 16 | implemented methods you can use `Vec` also as a regular slice. 17 | 18 | ```go 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/isgj/collection" 25 | ) 26 | 27 | func main() { 28 | strings := collection.Vec[string]{"str1", "str2"} 29 | 30 | for i := 3; i < 6; i++ { 31 | strings = append(strings, fmt.Sprintf("str%d", i)) 32 | } 33 | 34 | sliced := strings[2:4] 35 | indexed := strings[0] 36 | fmt.Printf("len=%d, cap=%d, indexed=%s, sliced=%v\n", len(strings), cap(strings), indexed, sliced) 37 | } 38 | // Output: 39 | // len=5, cap=8, indexed=str1, sliced=[str3 str4] 40 | ``` 41 | 42 | The most noticable method of `Vec` is `Iter` (or `ReverseIter`) which returns a lazy iterator over the 43 | elements of the slice. Check below. 44 | 45 | --- 46 | 47 | [collection.Iterator[T]](https://pkg.go.dev/github.com/isgj/collection#Iterator) is a lazy iterator over a list of values of type `T`. 48 | 49 | ```go 50 | package main 51 | 52 | import ( 53 | "fmt" 54 | 55 | "github.com/isgj/collection" 56 | ) 57 | 58 | func main() { 59 | first_slice := collection.Vec[int]{1, 2, 3, 4, 5} 60 | second_slice := collection.Vec[int]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14} 61 | 62 | result := first_slice. 63 | Iter(). 64 | FollowedBy(second_slice.ReverseIter()). 65 | Filter(func(item int) bool { 66 | fmt.Printf("will test item: %2d\n", item) 67 | return item%2 == 0 68 | }). 69 | Skip(2). 70 | Take(3). 71 | Collect() 72 | 73 | fmt.Printf("len=%d, cap=%d, vec=%v\n", result.Len(), result.Cap(), result) 74 | } 75 | // Output: 76 | // will test item: 1 77 | // will test item: 2 78 | // will test item: 3 79 | // will test item: 4 80 | // will test item: 5 81 | // will test item: 14 82 | // will test item: 12 83 | // will test item: 10 84 | // len=3, cap=4, vec=[14 12 10] 85 | ``` 86 | 87 | > To note: because the iterator is lazy the test of `Filter` is not called on each element, but only as much as needed (no wasted calls). 88 | > At the end you need to call `Collect` to get back a slice. If `Collect` is not called nothing gets executed. 89 | 90 | --- 91 | 92 | [collection.Map](https://pkg.go.dev/github.com/isgj/collection#Map) 93 | 94 | [collection.Set](https://pkg.go.dev/github.com/isgj/collection#Set) 95 | 96 | [collection.DLList](https://pkg.go.dev/github.com/isgj/collection#DLList) 97 | 98 | [collection.LRUCache](https://pkg.go.dev/github.com/isgj/collection#LRUCache) 99 | -------------------------------------------------------------------------------- /cache.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | // LRUCache implements a least recently used cache 4 | type LRUCache[K comparable, V any] struct { 5 | size int 6 | head *cnode[K, V] 7 | tail *cnode[K, V] 8 | cached map[K]*cnode[K, V] 9 | } 10 | 11 | // NewLRUCache creates a new LRUCache. 12 | // If the size is 0 or negative, the cache is unbounded. 13 | func NewCache[K comparable, V any](size int) *LRUCache[K, V] { 14 | return &LRUCache[K, V]{size: size, cached: make(map[K]*cnode[K, V])} 15 | } 16 | 17 | // Clear removes all items from the cache. 18 | func (c *LRUCache[K, V]) Clear() { 19 | c.cached = make(map[K]*cnode[K, V]) 20 | c.head = nil 21 | c.tail = nil 22 | } 23 | 24 | // Get returns the value for the given key if present in the cache. 25 | func (c *LRUCache[K, V]) Get(key K) (val V, ok bool) { 26 | node, ok := c.cached[key] 27 | if !ok { 28 | return val, ok 29 | } 30 | c.moveToHead(node) 31 | return node.val, ok 32 | } 33 | 34 | // GetOrAdd returns the value for the given key if present in the cache. 35 | // If not, it adds the value returned bu f and returns the given value. 36 | func (c *LRUCache[K, V]) GetOrAdd(key K, f func() V) V { 37 | node, ok := c.Get(key) 38 | if ok { 39 | return node 40 | } 41 | val := f() 42 | c.Put(key, val) 43 | return val 44 | } 45 | 46 | // IterKeys returns an iterator over the keys in the cache. 47 | // The keys are returned from the least recently used to the last one. 48 | func (c *LRUCache[K, V]) IterKeys() Iterator[K] { 49 | cur_node := c.head 50 | return func() (k K, ok bool) { 51 | if cur_node == nil { 52 | return k, false 53 | } 54 | k, cur_node = cur_node.key, cur_node.next 55 | return k, true 56 | } 57 | } 58 | 59 | // IterVals returns an iterator over the values in the cache. 60 | // The values are returned from the least recently used to the last one. 61 | func (c *LRUCache[K, V]) IterVals() Iterator[V] { 62 | cur_node := c.head 63 | return func() (v V, ok bool) { 64 | if cur_node == nil { 65 | return v, false 66 | } 67 | v, cur_node = cur_node.val, cur_node.next 68 | return v, true 69 | } 70 | } 71 | 72 | // IsFull returns true if the cache is full. 73 | func (c *LRUCache[K, V]) IsFull() bool { 74 | return len(c.cached) == c.size && c.size > 0 75 | } 76 | 77 | // IsEmpty returns true if the cache is empty. 78 | func (c *LRUCache[K, V]) IsEmpty() bool { 79 | return len(c.cached) == 0 80 | } 81 | 82 | // Len returns the number of items in the cache. 83 | func (c *LRUCache[K, V]) Len() int { 84 | return len(c.cached) 85 | } 86 | 87 | // Put adds the given key-value pair to the cache. 88 | func (c *LRUCache[K, V]) Put(key K, val V) { 89 | node, ok := c.cached[key] 90 | if ok { 91 | node.val = val 92 | c.moveToHead(node) 93 | return 94 | } 95 | if c.size > 0 && len(c.cached) >= c.size { 96 | c.removeTail() 97 | } 98 | node = &cnode[K, V]{key: key, val: val} 99 | c.cached[key] = node 100 | // Add the first node 101 | if c.head == nil { 102 | c.head, c.tail = node, node 103 | return 104 | } 105 | c.moveToHead(node) 106 | } 107 | 108 | // ReverseIterKeys returns an iterator over the keys in the cache. 109 | // The keys are returned from the last used to the least recently one. 110 | func (c *LRUCache[K, V]) ReverseIterKeys() Iterator[K] { 111 | cur_node := c.tail 112 | return func() (k K, ok bool) { 113 | if cur_node == nil { 114 | return k, false 115 | } 116 | k, cur_node = cur_node.key, cur_node.prev 117 | return k, true 118 | } 119 | } 120 | 121 | // IterVals returns an iterator over the values in the cache. 122 | // The values are returned from the last used to the least recently one. 123 | func (c *LRUCache[K, V]) ReverseIterVals() Iterator[V] { 124 | cur_node := c.tail 125 | return func() (v V, ok bool) { 126 | if cur_node == nil { 127 | return v, false 128 | } 129 | v, cur_node = cur_node.val, cur_node.prev 130 | return v, true 131 | } 132 | } 133 | 134 | func (c *LRUCache[K, V]) moveToHead(node *cnode[K, V]) { 135 | if node == c.head { 136 | return 137 | } 138 | if node == c.tail { 139 | c.tail = node.prev 140 | } 141 | if node.prev != nil { 142 | node.prev.next = node.next 143 | } 144 | if node.next != nil { 145 | node.next.prev = node.prev 146 | } 147 | node.prev = nil 148 | node.next = c.head 149 | c.head.prev = node 150 | c.head = node 151 | } 152 | 153 | func (c *LRUCache[K, V]) removeTail() { 154 | if c.tail == nil { 155 | return 156 | } 157 | if c.tail.prev != nil { 158 | c.tail.prev.next = nil 159 | } 160 | delete(c.cached, c.tail.key) 161 | c.tail = c.tail.prev 162 | } 163 | 164 | type cnode[K comparable, V any] struct { 165 | key K 166 | val V 167 | prev *cnode[K, V] 168 | next *cnode[K, V] 169 | } 170 | -------------------------------------------------------------------------------- /cache_test.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestNewCacheWithLimit(t *testing.T) { 8 | cache := NewCache[int, int](3) 9 | cache.Put(1, 1) 10 | cache.Put(2, 2) 11 | cache.Put(3, 3) 12 | cache.Put(4, 4) 13 | if cache.Len() != 3 { 14 | t.Errorf("cache.Size() = %d, want %d", cache.Len(), 3) 15 | } 16 | } 17 | 18 | func TestLeastUsedIsEvicted(t *testing.T) { 19 | cache := NewCache[int, int](3) 20 | cache.Put(1, 1) 21 | cache.Put(2, 2) 22 | cache.Put(3, 3) 23 | cache.Get(1) // 2 becomes the least used 24 | cache.Put(4, 4) 25 | if v, ok := cache.Get(2); ok { 26 | t.Errorf("cache.Get(1) = %d, want %d", v, 0) 27 | } 28 | } 29 | 30 | func TestCacheClear(t *testing.T) { 31 | cache := NewCache[int, int](3) 32 | cache.Put(1, 1) 33 | cache.Put(2, 2) 34 | cache.Put(3, 3) 35 | cache.Clear() 36 | if cache.Len() != 0 { 37 | t.Errorf("cache.Size() = %d, want %d", cache.Len(), 0) 38 | } 39 | } 40 | 41 | func TestCacheGet(t *testing.T) { 42 | cache := NewCache[int, int](3) 43 | cache.Put(1, 1) 44 | cache.Put(2, 2) 45 | cache.Put(3, 3) 46 | cache.Put(4, 4) // 1 is evicted 47 | if v, ok := cache.Get(1); ok { 48 | t.Errorf("cache.Get(1) = %d, %t, want %d, %t", v, ok, 0, false) 49 | } 50 | if v, ok := cache.Get(2); !ok || v != 2 { 51 | t.Errorf("cache.Get(2) = %d, %t, want %d, %t", v, ok, 2, true) 52 | } 53 | if v, ok := cache.Get(3); !ok || v != 3 { 54 | t.Errorf("cache.Get(3) = %d, %t, want %d, %t", v, ok, 3, true) 55 | } 56 | if v, ok := cache.Get(4); !ok || v != 4 { 57 | t.Errorf("cache.Get(4) = %d, %t, want %d, %t", v, ok, 4, true) 58 | } 59 | } 60 | 61 | func TestCacheGetOrAdd(t *testing.T) { 62 | cache := NewCache[int, int](3) 63 | cache.Put(1, 1) 64 | cache.Put(2, 2) 65 | cache.Put(3, 3) 66 | if v, ok := cache.Get(1); !ok || v != 1 { 67 | t.Errorf("cache.Get(1) = %d, %t, want %d, %t", v, ok, 1, true) 68 | } 69 | if v := cache.GetOrAdd(1, func() int { return 10 }); v != 1 { 70 | t.Errorf("cache.GetOrAdd(1) = %d, want %d", v, 1) 71 | } 72 | if v, ok := cache.Get(1); !ok || v != 1 { 73 | t.Errorf("cache.Get(1) = %d, %t, want %d, %t", v, ok, 1, true) 74 | } 75 | if v := cache.GetOrAdd(4, func() int { return 10 }); v != 10 { 76 | t.Errorf("cache.GetOrAdd(4) = %d, want %d", v, 10) 77 | } 78 | if v, ok := cache.Get(4); !ok || v != 10 { 79 | t.Errorf("cache.Get(4) = %d, %t, want %d, %t", v, ok, 10, false) 80 | } 81 | } 82 | 83 | func TestCacheLen(t *testing.T) { 84 | cache := NewCache[int, int](3) 85 | cache.Put(1, 1) 86 | cache.Put(2, 2) 87 | cache.Put(3, 3) 88 | if cache.Len() != 3 { 89 | t.Errorf("cache.Len() = %d, want %d", cache.Len(), 3) 90 | } 91 | cache.Put(4, 4) // 1 is evicted 92 | if cache.Len() != 3 { 93 | t.Errorf("cache.Len() = %d, want %d", cache.Len(), 3) 94 | } 95 | } 96 | 97 | func TestCachePutLeastUsed(t *testing.T) { 98 | cache := NewCache[int, int](3) 99 | cache.Put(1, 1) 100 | cache.Put(2, 2) 101 | cache.Put(3, 3) 102 | cache.Put(1, 10) // value of key 1 should be 10, and it should be the head 103 | cache.Put(4, 4) // 2 is evicted 104 | cache.Put(5, 5) // 3 is evicted 105 | if v, ok := cache.Get(1); !ok || v != 10 { 106 | t.Errorf("cache.Get(1) = %d, %t, want %d, %t", v, ok, 10, true) 107 | } 108 | if v, ok := cache.Get(2); ok { 109 | t.Errorf("cache.Get(2) = %d, %t, want %d, %t", v, ok, 0, false) 110 | } 111 | if v, ok := cache.Get(3); ok { 112 | t.Errorf("cache.Get(3) = %d, %t, want %d, %t", v, ok, 0, false) 113 | } 114 | if v, ok := cache.Get(4); !ok || v != 4 { 115 | t.Errorf("cache.Get(4) = %d, %t, want %d, %t", v, ok, 4, true) 116 | } 117 | if v, ok := cache.Get(5); !ok || v != 5 { 118 | t.Errorf("cache.Get(4) = %d, %t, want %d, %t", v, ok, 5, true) 119 | } 120 | if cache.Len() != 3 { 121 | t.Errorf("cache.Len() = %d, want %d", cache.Len(), 3) 122 | } 123 | } 124 | 125 | func TestCachePutLastUsed(t *testing.T) { 126 | cache := NewCache[int, int](3) 127 | cache.Put(1, 1) 128 | cache.Put(2, 2) 129 | cache.Put(3, 3) 130 | cache.Put(3, 10) 131 | cache.Put(4, 4) // 1 is evicted 132 | cache.Put(5, 5) // 2 is evicted 133 | if v, ok := cache.Get(1); ok { 134 | t.Errorf("cache.Get(1) = %d, %t, want %d, %t", v, ok, 0, false) 135 | } 136 | if v, ok := cache.Get(2); ok { 137 | t.Errorf("cache.Get(2) = %d, %t, want %d, %t", v, ok, 0, false) 138 | } 139 | if v, ok := cache.Get(3); !ok || v != 10 { 140 | t.Errorf("cache.Get(3) = %d, %t, want %d, %t", v, ok, 10, true) 141 | } 142 | if v, ok := cache.Get(4); !ok || v != 4 { 143 | t.Errorf("cache.Get(4) = %d, %t, want %d, %t", v, ok, 4, true) 144 | } 145 | if v, ok := cache.Get(5); !ok || v != 5 { 146 | t.Errorf("cache.Get(4) = %d, %t, want %d, %t", v, ok, 5, true) 147 | } 148 | if cache.Len() != 3 { 149 | t.Errorf("cache.Len() = %d, want %d", cache.Len(), 3) 150 | } 151 | } 152 | 153 | func TestCacheIterKeys(t *testing.T) { 154 | cache := NewCache[int, int](3) 155 | cache.Put(1, 4) 156 | cache.Put(2, 5) 157 | cache.Put(3, 6) 158 | keys := cache.IterKeys().Collect() 159 | for ind, v := range []int{3, 2, 1} { 160 | if keys[ind] != v { 161 | t.Errorf("cache.IterKeys()[%d] = %d, want %d", ind, keys[ind], v) 162 | } 163 | } 164 | } 165 | 166 | func TestCacheIterVales(t *testing.T) { 167 | cache := NewCache[int, int](3) 168 | cache.Put(1, 4) 169 | cache.Put(2, 5) 170 | cache.Put(3, 6) 171 | keys := cache.IterVals().Collect() 172 | for ind, v := range []int{6, 5, 4} { 173 | if keys[ind] != v { 174 | t.Errorf("cache.IterVals()[%d] = %d, want %d", ind, keys[ind], v) 175 | } 176 | } 177 | } 178 | 179 | func TestCacheReverseIterKeys(t *testing.T) { 180 | cache := NewCache[int, int](3) 181 | cache.Put(1, 4) 182 | cache.Put(2, 5) 183 | cache.Put(3, 6) 184 | keys := cache.ReverseIterKeys().Collect() 185 | for ind, v := range []int{1, 2, 3} { 186 | if keys[ind] != v { 187 | t.Errorf("cache.ReverseIterKeys()[%d] = %d, want %d", ind, keys[ind], v) 188 | } 189 | } 190 | } 191 | 192 | func TestCacheReverseIterVales(t *testing.T) { 193 | cache := NewCache[int, int](3) 194 | cache.Put(1, 4) 195 | cache.Put(2, 5) 196 | cache.Put(3, 6) 197 | keys := cache.ReverseIterVals().Collect() 198 | for ind, v := range []int{4, 5, 6} { 199 | if keys[ind] != v { 200 | t.Errorf("cache.ReverseIterVals()[%d] = %d, want %d", ind, keys[ind], v) 201 | } 202 | } 203 | } 204 | 205 | func TestCacheIsEmpty(t *testing.T) { 206 | cache := NewCache[int, int](3) 207 | if !cache.IsEmpty() { 208 | t.Errorf("cache.IsEmpty() = %t, want %t", cache.IsEmpty(), true) 209 | } 210 | cache.Put(1, 4) 211 | if cache.IsEmpty() { 212 | t.Errorf("cache.IsEmpty() = %t, want %t", cache.IsEmpty(), false) 213 | } 214 | } 215 | 216 | func TestCacheIsFullWithSize(t *testing.T) { 217 | cache := NewCache[int, int](3) 218 | if cache.IsFull() { 219 | t.Errorf("cache.IsFull() = %t, want %t", cache.IsFull(), false) 220 | } 221 | cache.Put(1, 4) 222 | cache.Put(2, 5) 223 | cache.Put(3, 6) 224 | if !cache.IsFull() { 225 | t.Errorf("cache.IsFull() = %t, want %t", cache.IsFull(), true) 226 | } 227 | } 228 | 229 | func TestCacheIsFullWithoutSize(t *testing.T) { 230 | cache := NewCache[int, int](0) 231 | if cache.IsFull() { 232 | t.Errorf("cache.IsFull() = %t, want %t", cache.IsFull(), false) 233 | } 234 | cache.Put(1, 4) 235 | cache.Put(2, 5) 236 | cache.Put(3, 6) 237 | if cache.IsFull() { 238 | t.Errorf("cache.IsFull() = %t, want %t", cache.IsFull(), false) 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // collection implements generic data structures in GO. 2 | // collection.Vec and collection.Map are the same as native `slice` and `map` but with some methods. 3 | // collection.Set is a set of comparable values. It is implemented as a map where the values are an empty struct. 4 | // collection.DLList is a doubly linked list which can be used as a stack or a queue. It has fast O(1) operations in both ends. 5 | // collection.Iterator is a lazy generic iterator. It is not limited to the structures defined in this package. 6 | package collection 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/isgj/collection 2 | 3 | go 1.18 4 | 5 | require golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 // indirect 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 h1:ba9YlqfDGTTQ5aZ2fwOoQ1hf32QySyQkR6ODGDzHlnE= 2 | golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= 3 | -------------------------------------------------------------------------------- /iter/iterators.go: -------------------------------------------------------------------------------- 1 | // Utility iterator functions. 2 | package iter 3 | 4 | import ( 5 | c "github.com/isgj/collection" 6 | "golang.org/x/exp/constraints" 7 | ) 8 | 9 | // Map will map values of type `I` from to type `O` through the `to` mapper function. 10 | // 11 | // Example: 12 | // v := collection.Vec[int]{1, 2, 3, 4, 5, 6, 7, 8, 9} 13 | // number_iterator := v.Iter().Filter(func(item int) string { return item%2 == 0}) 14 | // iter.Map(number_iterator, func(item int) string { return fmt.Sprint("number ", item) }). 15 | // ForEach(func(item string) { fmt.Prinln(item) }) 16 | // 17 | // This is a workaround to implement `Map` as currently methods cannot have type paramters. 18 | func Map[I any, O any](it c.Iterator[I], to func(item I) O) c.Iterator[O] { 19 | return func() (O, bool) { 20 | if i, ok := it(); ok { 21 | return to(i), true 22 | } 23 | return *new(O), false 24 | } 25 | } 26 | 27 | // Reduce will reduce the values through the reducer 28 | // 29 | // This is a workaround to implement `Reduce` as currently methods cannot have type paramters. 30 | func Reduce[I any, O any](it c.Iterator[I], start O, reducer func(acc O, item I) O) O { 31 | for i, ok := it(); ok; i, ok = it() { 32 | start = reducer(start, i) 33 | } 34 | return start 35 | } 36 | 37 | // Sum will return the sum of the values. If the iterated values are strings it will concatenate them 38 | func Sum[T constraints.Ordered](it c.Iterator[T]) T { 39 | result, _ := it() 40 | for i, ok := it(); ok; i, ok = it() { 41 | result += i 42 | } 43 | return result 44 | } 45 | 46 | func FromSlice[T any](s []T) c.Iterator[T] { 47 | return c.Vec[T](s).Iter() 48 | } 49 | 50 | // Iterate from 0 to `end` by steps of 1 51 | func Range(end int) c.Iterator[int] { 52 | return XRange(0, end, 1) 53 | } 54 | 55 | // Iterate from `start` to `end` by steps of `step`. 56 | // Currently the range will only increase, the step will be added for each iteration so make sure that it will 57 | // eventually reach the end, otherwise you enter an infinite loop. 58 | func XRange(start, end, step int) c.Iterator[int] { 59 | return func() (int, bool) { 60 | if start < end { 61 | r := start 62 | start += step 63 | return r, true 64 | } 65 | return 0, false 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /iterator.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | // Iterator is a lazy iterator over generic data types. 4 | // It can be called several times to produce values. 5 | // When the second returned value is `true` means the value is valid and it can be consumed. 6 | // When the second returned value is `false` means the value is not valid. 7 | // In this case the zero value of the type `T` is returned and it should not be consumed. 8 | // Consecutive calls after the first time `false` is returned, should return the same values. 9 | // 10 | // Iterator is not limited to the structures defined in this package. The source of the iterated values can be anything. 11 | // Ex: think of iterating over the lines of a file without having to load the whole file into memory, or iterating over the cursor of a database. 12 | type Iterator[T any] func() (T, bool) 13 | 14 | // Any returns true as soon as a value satisfies the test, false otherwise. 15 | func (it Iterator[T]) Any(test func(item T) bool) bool { 16 | for i, ok := it(); ok; i, ok = it() { 17 | if test(i) { 18 | return true 19 | } 20 | } 21 | return false 22 | } 23 | 24 | // Collect will consume the iterator and return a `Vec` with all the values. 25 | func (it Iterator[T]) Collect() Vec[T] { 26 | var vec Vec[T] 27 | for i, ok := it(); ok; i, ok = it() { 28 | vec = append(vec, i) 29 | } 30 | return vec 31 | } 32 | 33 | // Count will consume the iterator and return the number of values iterated. 34 | func (it Iterator[T]) Count() int { 35 | var c int 36 | for _, ok := it(); ok; _, ok = it() { 37 | c++ 38 | } 39 | return c 40 | } 41 | 42 | // Every will return false as soon as a value will fail the test, true otherwise. 43 | func (it Iterator[T]) Every(test func(item T) bool) bool { 44 | for i, ok := it(); ok; i, ok = it() { 45 | if !test(i) { 46 | return false 47 | } 48 | } 49 | return true 50 | } 51 | 52 | // Filter will pass only the values that satisfy the test. 53 | func (it Iterator[T]) Filter(test func(item T) bool) Iterator[T] { 54 | return func() (T, bool) { 55 | for i, ok := it(); ok; i, ok = it() { 56 | if test(i) { 57 | return i, ok 58 | } 59 | } 60 | return *new(T), false 61 | } 62 | } 63 | 64 | // Find will try to find a value that satisfies the test. 65 | // The second returned value is true if a value was found, false otherwise. 66 | func (it Iterator[T]) Find(test func(item T) bool) (T, bool) { 67 | for i, ok := it(); ok; i, ok = it() { 68 | if test(i) { 69 | return i, ok 70 | } 71 | } 72 | return *new(T), false 73 | } 74 | 75 | // FollowedBy will yield first the values of `it` followed by the values of `other`. 76 | func (it Iterator[T]) FollowedBy(other Iterator[T]) Iterator[T] { 77 | other_turn := false 78 | return func() (T, bool) { 79 | if other_turn { 80 | return other() 81 | } 82 | if i, ok := it(); ok { 83 | return i, ok 84 | } 85 | other_turn = true 86 | return other() 87 | } 88 | } 89 | 90 | // ForEach will consume the iterator and run the action with every value. 91 | func (it Iterator[T]) ForEach(action func(item T)) { 92 | for i, ok := it(); ok; i, ok = it() { 93 | action(i) 94 | } 95 | } 96 | 97 | // Skip will skip the first `count` values 98 | func (it Iterator[T]) Skip(count int) Iterator[T] { 99 | skipped := false 100 | return func() (T, bool) { 101 | if skipped { 102 | return it() 103 | } 104 | for i := 0; i < count; i++ { 105 | if _, ok := it(); !ok { 106 | skipped = true 107 | return *new(T), false 108 | } 109 | } 110 | skipped = true 111 | return it() 112 | } 113 | } 114 | 115 | // SkipWhile will skip the first elements that satisfy the test. 116 | func (it Iterator[T]) SkipWhile(test func(item T) bool) Iterator[T] { 117 | skipped := false 118 | return func() (T, bool) { 119 | if !skipped { 120 | skipped = true 121 | return it.Find(func(item T) bool { return !test(item) }) 122 | } 123 | return it() 124 | } 125 | } 126 | 127 | // Take will yield at most the first `count` values. 128 | func (it Iterator[T]) Take(count int) Iterator[T] { 129 | taken := 0 130 | return func() (T, bool) { 131 | if taken >= count { 132 | return *new(T), false 133 | } 134 | if i, ok := it(); ok { 135 | taken++ 136 | return i, ok 137 | } 138 | taken = count 139 | return *new(T), false 140 | } 141 | } 142 | 143 | // TakeWhile will stop at the first value that does not satisfy the test. 144 | func (it Iterator[T]) TakeWhile(test func(item T) bool) Iterator[T] { 145 | stopped := false 146 | return func() (T, bool) { 147 | if stopped { 148 | return *new(T), false 149 | } 150 | if i, ok := it(); ok && test(i) { 151 | return i, ok 152 | } 153 | stopped = true 154 | return *new(T), false 155 | } 156 | } 157 | 158 | // Tap will run `action` with every value that will pass through the iterator. 159 | func (it Iterator[T]) Tap(action func(item T)) Iterator[T] { 160 | return func() (T, bool) { 161 | i, ok := it() 162 | if ok { 163 | action(i) 164 | } 165 | return i, ok 166 | } 167 | } 168 | 169 | // Reverse will consume the iterator, collect the values in a `Vec` and iterate in reverse those values. 170 | // Since `Reverse` will consume the iterator and allocate a `Vec`, when possible use `Vec.ReverseIter`. 171 | func (it Iterator[T]) Reverse() Iterator[T] { 172 | return it.Collect().ReverseIter() 173 | } 174 | -------------------------------------------------------------------------------- /iterator_test.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | import "testing" 4 | 5 | func TestAny(t *testing.T) { 6 | var a Vec[int] 7 | a = a.AppendAll(1, 2, 3) 8 | if !a.Iter().Any(func(i int) bool { return i == 1 }) { 9 | t.Errorf("expected true, got false") 10 | } 11 | if a.Iter().Any(func(i int) bool { return i == 4 }) { 12 | t.Errorf("expected false, got true") 13 | } 14 | } 15 | 16 | func TestCollect(t *testing.T) { 17 | var a Vec[int] 18 | a = a.AppendAll(1, 2, 3) 19 | b := a.Iter().Collect() 20 | if b.Len() != 3 { 21 | t.Errorf("expected 3, got %d", b.Len()) 22 | } 23 | } 24 | 25 | func TestCount(t *testing.T) { 26 | var a Vec[int] 27 | a = a.AppendAll(1, 2, 3) 28 | if a.Iter().Count() != 3 { 29 | t.Errorf("expected 3, got %d", a.Iter().Count()) 30 | } 31 | } 32 | 33 | func TestEvery(t *testing.T) { 34 | var a Vec[int] 35 | a = a.AppendAll(1, 2, 3) 36 | if !a.Iter().Every(func(i int) bool { return i > 0 }) { 37 | t.Errorf("expected true, got false") 38 | } 39 | if a.Iter().Every(func(i int) bool { return i == 0 }) { 40 | t.Errorf("expected false, got true") 41 | } 42 | } 43 | 44 | func TestFilter(t *testing.T) { 45 | var a Vec[int] 46 | a = a.AppendAll(1, 2, 3) 47 | b := a.Iter().Filter(func(i int) bool { return i > 1 }).Collect() 48 | if b.Len() != 2 { 49 | t.Errorf("expected 2, got %d", b.Len()) 50 | } 51 | } 52 | 53 | func TestFind(t *testing.T) { 54 | var a Vec[int] 55 | a = a.AppendAll(1, 2, 3) 56 | i, ok := a.Iter().Find(func(i int) bool { return i == 2 }) 57 | if !ok { 58 | t.Errorf("expected true, got false") 59 | } 60 | if i != 2 { 61 | t.Errorf("expected 2, got %d", i) 62 | } 63 | _, ok = a.Iter().Find(func(i int) bool { return i == 4 }) 64 | if ok { 65 | t.Errorf("expected false, got true") 66 | } 67 | } 68 | 69 | func TestFollowedBy(t *testing.T) { 70 | var a Vec[int] 71 | a = a.AppendAll(1, 2, 3) 72 | b := a.Iter().FollowedBy(a.Iter()).Collect() 73 | if b.Len() != 6 { 74 | t.Errorf("expected 6, got %d", b.Len()) 75 | } 76 | } 77 | 78 | func TestForEach(t *testing.T) { 79 | var a Vec[int] 80 | a = a.AppendAll(1, 2, 3) 81 | ind := -1 82 | a.Iter().ForEach(func(i int) { 83 | ind++ 84 | if i != a[ind] { 85 | t.Errorf("expected %d, got %d", a[ind], i) 86 | } 87 | }) 88 | if ind != 2 { 89 | t.Errorf("expected 2, got %d", ind) 90 | } 91 | } 92 | 93 | func TestSkip(t *testing.T) { 94 | var a Vec[int] 95 | a = a.AppendAll(1, 2, 3) 96 | b := a.Iter().Skip(1).Collect() 97 | if b.Len() != 2 { 98 | t.Errorf("expected 2, got %d", b.Len()) 99 | } 100 | } 101 | 102 | func TestSkipWhile(t *testing.T) { 103 | var a Vec[int] 104 | a = a.AppendAll(1, 2, 3) 105 | b := a.Iter().SkipWhile(func(i int) bool { return i < 2 }).Collect() 106 | if b.Len() != 2 { 107 | t.Errorf("expected 2, got %d", b.Len()) 108 | } 109 | } 110 | 111 | func TestTake(t *testing.T) { 112 | var a Vec[int] 113 | a = a.AppendAll(1, 2, 3) 114 | b := a.Iter().Take(2).Collect() 115 | if b.Len() != 2 { 116 | t.Errorf("expected 2, got %d", b.Len()) 117 | } 118 | } 119 | 120 | func TestTakeWhile(t *testing.T) { 121 | var a Vec[int] 122 | a = a.AppendAll(1, 2, 3) 123 | b := a.Iter().TakeWhile(func(i int) bool { return i < 3 }).Collect() 124 | if b.Len() != 2 { 125 | t.Errorf("expected 2, got %d", b.Len()) 126 | } 127 | } 128 | 129 | func TestTap(t *testing.T) { 130 | var a Vec[int] 131 | a = a.AppendAll(1, 2, 3) 132 | ind := -1 133 | a.Iter().Tap(func(i int) { 134 | ind++ 135 | if i != a[ind] { 136 | t.Errorf("expected %d, got %d", a[ind], i) 137 | } 138 | }).ForEach(func(i int) {}) 139 | if ind != 2 { 140 | t.Errorf("expected 2, got %d", ind) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /linked_list.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | // DLList is a doubly linked list. It is based on the linked list implementation. 4 | // It can be used as a stack and/or a queue. 5 | // All the operations are O(1), even when the size of the list is large. 6 | // 7 | // The zero value for DLList is an empty list ready to use. 8 | // var queue collection.DLList[int] 9 | // queue.PushBack(1) 10 | // queue.PushBack(2) 11 | // queue.PushBack(3) 12 | // fmt.Println(queue.PopFront()) // 1, true 13 | // fmt.Println(queue.PopFront()) // 2, true 14 | // fmt.Println(queue.PopFront()) // 3, true 15 | // fmt.Println(queue.PopFront()) // 0, false 16 | type DLList[T any] struct { 17 | head *node[T] 18 | tail *node[T] 19 | size int 20 | } 21 | 22 | // Back returns the last element of the list. 23 | // If the list is empty, the zero value is returned and false. 24 | func (ll *DLList[T]) Back() (T, bool) { 25 | if ll.size == 0 { 26 | return *new(T), false 27 | } 28 | return ll.tail.value, true 29 | } 30 | 31 | // Clear removes all elements from the list. 32 | func (ll *DLList[T]) Clear() { 33 | ll.head = nil 34 | ll.tail = nil 35 | ll.size = 0 36 | } 37 | 38 | // Front returns the first element of the list. 39 | // If the list is empty, the zero value is returned and false. 40 | func (ll *DLList[T]) Front() (T, bool) { 41 | if ll.size == 0 { 42 | return *new(T), false 43 | } 44 | return ll.head.value, true 45 | } 46 | 47 | // IsEmpty returns true if the list is empty. 48 | func (ll *DLList[T]) IsEmpty() bool { 49 | return ll.size == 0 50 | } 51 | 52 | // Iter returns a new iterator for the list, iterating the values from front to back. 53 | func (ll *DLList[T]) Iter() Iterator[T] { 54 | cur_node := ll.head 55 | return func() (T, bool) { 56 | if cur_node == nil { 57 | return *new(T), false 58 | } 59 | v := cur_node.value 60 | cur_node = cur_node.next 61 | return v, true 62 | } 63 | } 64 | 65 | // Len returns the number of elements in the list. 66 | func (ll *DLList[T]) Len() int { 67 | return ll.size 68 | } 69 | 70 | // PopBack removes the last element from the list. 71 | // If the second return value is false, the list is empty and the zero value is returned. 72 | func (ll *DLList[T]) PopBack() (T, bool) { 73 | if ll.size == 0 { 74 | return *new(T), false 75 | } 76 | n := ll.tail 77 | if ll.size == 1 { 78 | ll.head = nil 79 | ll.tail = nil 80 | } else { 81 | ll.tail = n.prev 82 | ll.tail.next = nil 83 | } 84 | ll.size-- 85 | return n.value, true 86 | } 87 | 88 | // PopFront removes the first element from the list. 89 | // If the second return value is false, the list is empty and the zero value is returned. 90 | func (ll *DLList[T]) PopFront() (T, bool) { 91 | if ll.size == 0 { 92 | return *new(T), false 93 | } 94 | n := ll.head 95 | if ll.size == 1 { 96 | ll.head = nil 97 | ll.tail = nil 98 | } else { 99 | ll.head = n.next 100 | ll.head.prev = nil 101 | } 102 | ll.size-- 103 | return n.value, true 104 | } 105 | 106 | // PushBack adds a new element at the back of the list. 107 | func (ll *DLList[T]) PushBack(v T) { 108 | n := &node[T]{value: v} 109 | if ll.size == 0 { 110 | ll.head = n 111 | ll.tail = n 112 | } else { 113 | ll.tail.next = n 114 | n.prev = ll.tail 115 | ll.tail = n 116 | } 117 | ll.size++ 118 | } 119 | 120 | // PushFront adds a new element at the front of the list. 121 | func (ll *DLList[T]) PushFront(v T) { 122 | n := &node[T]{value: v} 123 | if ll.size == 0 { 124 | ll.head = n 125 | ll.tail = n 126 | } else { 127 | ll.head.prev = n 128 | n.next = ll.head 129 | ll.head = n 130 | } 131 | ll.size++ 132 | } 133 | 134 | // ReverseIter returns a new iterator for the list, iterating the values from back to front. 135 | func (ll *DLList[T]) ReverseIter() Iterator[T] { 136 | cur_node := ll.tail 137 | return func() (T, bool) { 138 | if cur_node == nil { 139 | return *new(T), false 140 | } 141 | v := cur_node.value 142 | cur_node = cur_node.prev 143 | return v, true 144 | } 145 | } 146 | 147 | // Size returns the number of elements in the list. 148 | // 149 | // Deprecated: Size is deprecated, use Len instead. 150 | func (ll *DLList[T]) Size() int { 151 | return ll.size 152 | } 153 | 154 | // node is a helper struct that holds the value and the links to the next and previous nodes. 155 | type node[T any] struct { 156 | value T 157 | prev *node[T] 158 | next *node[T] 159 | } 160 | -------------------------------------------------------------------------------- /linked_list_test.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | import "testing" 4 | 5 | func TestBack(t *testing.T) { 6 | var queue DLList[int] 7 | queue.PushBack(1) 8 | queue.PushBack(2) 9 | queue.PushBack(3) 10 | s := queue.Size() 11 | if v, ok := queue.Back(); !ok || v != 3 { 12 | t.Errorf("queue.Back() = %v, %v, want %v, %v", v, ok, 3, true) 13 | } 14 | if s != queue.Size() { 15 | t.Errorf("queue.size cahnged was = %d, now = %d", s, queue.Size()) 16 | } 17 | } 18 | 19 | func TestClear(t *testing.T) { 20 | var queue DLList[int] 21 | queue.PushBack(1) 22 | queue.PushBack(2) 23 | queue.PushBack(3) 24 | queue.Clear() 25 | if !queue.IsEmpty() { 26 | t.Errorf("queue.IsEmpty() = %v, want %v", queue.IsEmpty(), true) 27 | } 28 | if queue.head != nil { 29 | t.Errorf("queue.head = %v, want %v", queue.head, nil) 30 | } 31 | if queue.tail != nil { 32 | t.Errorf("queue.tail = %v, want %v", queue.tail, nil) 33 | } 34 | } 35 | 36 | func TestFront(t *testing.T) { 37 | var queue DLList[int] 38 | queue.PushBack(1) 39 | queue.PushBack(2) 40 | queue.PushBack(3) 41 | s := queue.Size() 42 | if v, ok := queue.Front(); !ok || v != 1 { 43 | t.Errorf("queue.Front() = %v, %v, want %v, %v", v, ok, 1, true) 44 | } 45 | if s != queue.Size() { 46 | t.Errorf("queue.size cahnged was = %d, now = %d", s, queue.Size()) 47 | } 48 | } 49 | 50 | func TestIsEmpty(t *testing.T) { 51 | var queue DLList[int] 52 | if !queue.IsEmpty() { 53 | t.Errorf("queue.IsEmpty() = %v, want %v", queue.IsEmpty(), true) 54 | } 55 | queue.PushBack(1) 56 | if queue.IsEmpty() { 57 | t.Errorf("queue.IsEmpty() = %v, want %v", queue.IsEmpty(), false) 58 | } 59 | } 60 | 61 | func TestDLLIter(t *testing.T) { 62 | var queue DLList[int] 63 | queue.PushBack(1) 64 | queue.PushBack(2) 65 | queue.PushBack(3) 66 | var i int 67 | queue.Iter().ForEach(func(v int) { 68 | if v != i+1 { 69 | t.Errorf("queue.Iter().ForEach() = %v, want %v", v, i+1) 70 | } 71 | i++ 72 | }) 73 | if i != 3 { 74 | t.Errorf("queue.Iter().ForEach() = %v, want %v", i, 3) 75 | } 76 | i = 3 77 | queue.ReverseIter().ForEach(func(v int) { 78 | if v != i { 79 | t.Errorf("queue.ReverseIter().ForEach() = %v, want %v", v, i) 80 | } 81 | i-- 82 | }) 83 | if i != 0 { 84 | t.Errorf("queue.ReverseIter().ForEach() = %v, want %v", i, 0) 85 | } 86 | } 87 | 88 | func TestPopBack(t *testing.T) { 89 | var queue DLList[int] 90 | queue.PushBack(1) 91 | queue.PushBack(2) 92 | queue.PushBack(3) 93 | s := queue.Size() 94 | if v, ok := queue.PopBack(); !ok || v != 3 { 95 | t.Errorf("queue.PopBack() = %v, %v, want %v, %v", v, ok, 3, true) 96 | } 97 | if s != queue.Size()+1 { 98 | t.Errorf("queue.size did not change, before = %d, now = %d", s, queue.Size()) 99 | } 100 | } 101 | 102 | func TestPopFront(t *testing.T) { 103 | var queue DLList[int] 104 | queue.PushBack(1) 105 | queue.PushBack(2) 106 | queue.PushBack(3) 107 | s := queue.Size() 108 | if v, ok := queue.PopFront(); !ok || v != 1 { 109 | t.Errorf("queue.PopFront() = %v, %v, want %v, %v", v, ok, 1, true) 110 | } 111 | if s != queue.Size()+1 { 112 | t.Errorf("queue.size did not change, before = %d, now = %d", s, queue.Size()) 113 | } 114 | } 115 | 116 | func TestPushBack(t *testing.T) { 117 | var queue DLList[int] 118 | queue.PushBack(1) 119 | queue.PushBack(2) 120 | queue.PushBack(3) 121 | s := queue.Size() 122 | if s != 3 { 123 | t.Errorf("queue.size = %d, want %d", s, 3) 124 | } 125 | if queue.head.value != 1 { 126 | t.Errorf("queue.head.value = %d, want %d", queue.head.value, 1) 127 | } 128 | if queue.tail.value != 3 { 129 | t.Errorf("queue.tail.value = %d, want %d", queue.tail.value, 3) 130 | } 131 | } 132 | 133 | func TestPushFront(t *testing.T) { 134 | var queue DLList[int] 135 | queue.PushFront(1) 136 | queue.PushFront(2) 137 | queue.PushFront(3) 138 | s := queue.Size() 139 | if s != 3 { 140 | t.Errorf("queue.size = %d, want %d", s, 3) 141 | } 142 | if queue.head.value != 3 { 143 | t.Errorf("queue.head.value = %d, want %d", queue.head.value, 3) 144 | } 145 | if queue.tail.value != 1 { 146 | t.Errorf("queue.tail.value = %d, want %d", queue.tail.value, 1) 147 | } 148 | } 149 | 150 | func TestLen(t *testing.T) { 151 | var queue DLList[int] 152 | queue.PushBack(1) 153 | queue.PushBack(2) 154 | queue.PushBack(3) 155 | s := queue.Len() 156 | if s != 3 { 157 | t.Errorf("queue.len = %d, want %d", s, 3) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /map.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | // Map is the same as `map` but with some methods. 4 | type Map[K comparable, V any] map[K]V 5 | 6 | // Clear will delete all the key/value pairs in the map. 7 | func (m Map[K, V]) Clear() { 8 | for k := range m { 9 | delete(m, k) 10 | } 11 | } 12 | 13 | // Has checks if the key is in the map. 14 | func (m Map[K, V]) Has(key K) bool { 15 | _, ok := m[key] 16 | return ok 17 | } 18 | 19 | // Keys will return a Vec with the keys of the map. 20 | func (m Map[K, V]) Keys() Vec[K] { 21 | v := make(Vec[K], 0, len(m)) 22 | for k := range m { 23 | v = append(v, k) 24 | } 25 | return v 26 | } 27 | 28 | // Len is an alias to the `len` function. 29 | func (m Map[K, V]) Len() int { 30 | return len(m) 31 | } 32 | 33 | // IsEmpty checks if the map has no entries 34 | func (m Map[K, V]) IsEmpty() bool { 35 | return len(m) == 0 36 | } 37 | 38 | // Values will return a Vec with the values of the map. 39 | func (m Map[K, V]) Values() Vec[V] { 40 | v := make(Vec[V], 0, len(m)) 41 | for _, val := range m { 42 | v = append(v, val) 43 | } 44 | return v 45 | } 46 | -------------------------------------------------------------------------------- /set.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | // Set is the classic `set` data structure 4 | type Set[T comparable] Map[T, struct{}] 5 | 6 | // Add will add the element to the set 7 | func (s Set[T]) Add(element T) { 8 | s[element] = struct{}{} 9 | } 10 | 11 | // Clear will delete all the set elements. 12 | func (s Set[T]) Clear() { 13 | for k := range s { 14 | delete(s, k) 15 | } 16 | } 17 | 18 | // Delete will remove the element from the set 19 | func (s Set[T]) Delete(elem T) { 20 | delete(s, elem) 21 | } 22 | 23 | // Difference will return a new set that will contain only the elements of the receiver that are not in the other 24 | func (s Set[T]) Difference(other Set[T]) Set[T] { 25 | result := Set[T]{} 26 | for elem := range s { 27 | if !other.Has(elem) { 28 | result.Add(elem) 29 | } 30 | } 31 | return result 32 | } 33 | 34 | // Has checks if the element is in the set. 35 | func (m Set[T]) Has(element T) bool { 36 | _, ok := m[element] 37 | return ok 38 | } 39 | 40 | // Intersection will return a new set with all the elements that are part of both the sets 41 | func (s Set[T]) Intersection(other Set[T]) Set[T] { 42 | result := Set[T]{} 43 | if s.Len() > other.Len() { 44 | s, other = other, s 45 | } 46 | for elem := range s { 47 | if other.Has(elem) { 48 | result.Add(elem) 49 | } 50 | } 51 | return result 52 | } 53 | 54 | // IsEmpty checks if the set is empty 55 | func (s Set[T]) IsEmpty() bool { 56 | return len(s) == 0 57 | } 58 | 59 | // Len is an alias to `len` 60 | func (s Set[T]) Len() int { 61 | return len(s) 62 | } 63 | 64 | // ToVec will collect the elements of the set to a `Vec` 65 | func (s Set[T]) ToVec() Vec[T] { 66 | v := make(Vec[T], 0, s.Len()) 67 | for e := range s { 68 | v = append(v, e) 69 | } 70 | return v 71 | } 72 | 73 | // Union returns a new set with all the the elements of both sets 74 | func (s Set[T]) Union(other Set[T]) Set[T] { 75 | result := Set[T]{} 76 | for e := range s { 77 | result.Add(e) 78 | } 79 | for e := range other { 80 | result.Add(e) 81 | } 82 | return result 83 | } 84 | 85 | // NewSet returns a new set from the list of values 86 | func NewSet[T comparable](values ...T) Set[T] { 87 | set := Set[T]{} 88 | for _, v := range values { 89 | set.Add(v) 90 | } 91 | return set 92 | } 93 | 94 | // NewSetFromIter will collect all the values of the iterator to a set 95 | func NewSetFromIter[T comparable](it Iterator[T]) Set[T] { 96 | set := Set[T]{} 97 | for v, ok := it(); ok; v, ok = it() { 98 | set.Add(v) 99 | } 100 | return set 101 | } 102 | -------------------------------------------------------------------------------- /vector.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | // Vec is a generic slice, the same rules of the native slice aplly also to `Vec`. 4 | type Vec[T any] []T 5 | 6 | // Len returns the length of the slice. It's an alias to the global `len` 7 | func (v Vec[T]) Len() int { 8 | return len(v) 9 | } 10 | 11 | // Cap returns the length of the slice. It's an alias to the global `cap` 12 | func (v Vec[T]) Cap() int { 13 | return cap(v) 14 | } 15 | 16 | // Append will add the item at the end of the slice. It's an alias to the global `append`. 17 | // Remember to assign the returned value, as you should do with `append`. 18 | func (v Vec[T]) Append(item T) Vec[T] { 19 | return append(v, item) 20 | } 21 | 22 | // AppendAll will add all the items at the end of the slice. 23 | func (v Vec[T]) AppendAll(items ...T) Vec[T] { 24 | for _, i := range items { 25 | v = append(v, i) 26 | } 27 | return v 28 | } 29 | 30 | // AppendIter will consume the iterator and append the yield values. 31 | func (v Vec[T]) AppendIter(it Iterator[T]) Vec[T] { 32 | for i, ok := it(); ok; i, ok = it() { 33 | v = append(v, i) 34 | } 35 | return v 36 | } 37 | 38 | // Iter will return a lazy iterator over the values of the slice. 39 | func (v Vec[T]) Iter() Iterator[T] { 40 | current := 0 41 | return func() (T, bool) { 42 | if current < len(v) { 43 | current++ 44 | return v[current-1], true 45 | } 46 | return *new(T), false 47 | } 48 | } 49 | 50 | // IsEmpty checks if the slice has no entries. 51 | func (v Vec[T]) IsEmpty() bool { 52 | return len(v) == 0 53 | } 54 | 55 | // ReverseIter will return a lazy iterator over the values of the slice in reverse order. 56 | func (v Vec[T]) ReverseIter() Iterator[T] { 57 | current := len(v) 58 | return func() (T, bool) { 59 | if current > 0 { 60 | current-- 61 | return v[current], true 62 | } 63 | return *new(T), false 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /vector_test.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | import "testing" 4 | 5 | func TestAppendAll(t *testing.T) { 6 | var a Vec[int] 7 | a = a.AppendAll(1, 2, 3) 8 | if a.Len() != 3 { 9 | t.Errorf("expected 3, got %d", a.Len()) 10 | } 11 | } 12 | 13 | func TestAppenIter(t *testing.T) { 14 | var a Vec[int] 15 | i := -1 16 | 17 | a = a.AppendIter(func() (int, bool) { 18 | i++ 19 | return 1, i < 3 20 | }) 21 | if a.Len() != 3 { 22 | t.Errorf("expected 3, got %d", a.Len()) 23 | } 24 | } 25 | 26 | func TestIter(t *testing.T) { 27 | var a Vec[int] 28 | a = a.AppendAll(1, 2, 3) 29 | it := a.Iter() 30 | // consume the iterator 31 | for i := 0; i < 3; i++ { 32 | if v, ok := it(); !ok { 33 | t.Errorf("expected ok, got %v", ok) 34 | } else if v != i+1 { 35 | t.Errorf("expected %d, got %d", i+1, v) 36 | } 37 | } 38 | // returns zero value and false, end of iteration 39 | if v, ok := it(); ok { 40 | t.Errorf("expected !ok, got %v", ok) 41 | } else if v != 0 { 42 | t.Errorf("expected 0, got %d", v) 43 | } 44 | } 45 | 46 | func TestReverseIter(t *testing.T) { 47 | var a Vec[int] 48 | a = a.AppendAll(1, 2, 3) 49 | it := a.ReverseIter() 50 | // consume the iterator 51 | for i := 3; i > 0; i-- { 52 | if v, ok := it(); !ok { 53 | t.Errorf("expected ok, got %v", ok) 54 | } else if v != i { 55 | t.Errorf("expected %d, got %d", i, v) 56 | } 57 | } 58 | // returns zero value and false, end of iteration 59 | if v, ok := it(); ok { 60 | t.Errorf("expected !ok, got %v", ok) 61 | } else if v != 0 { 62 | t.Errorf("expected 0, got %d", v) 63 | } 64 | } 65 | --------------------------------------------------------------------------------