├── .gitignore ├── LICENSE ├── README.md ├── collections.go ├── interfaces.go └── interfaces_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alessandro Diaferia 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-collections 2 | This project ports **map**-**filter**-**reduce** feature to [Go](http://golang.org). 3 | 4 | Supported features 5 | ------------------ 6 | 7 | ### Map 8 | 9 | Given a collection it's trivial creating a new one editing each element on the fly. 10 | 11 | ```go 12 | planets := []interface{}{ 13 | "Mercury", 14 | "Venus", 15 | "Earth", 16 | "Mars", 17 | "Jupiter", 18 | "Saturn", 19 | "Uranus", 20 | "Neptune", 21 | "Pluto", 22 | } 23 | 24 | coll := collections.NewFromSlice(planets) 25 | coll = coll.Map(func(v interface{}) interface{} { 26 | return strings.Join([]string{ "Hello ", v.(string) }) 27 | }) 28 | 29 | fmt.Println(coll) 30 | ``` 31 | 32 | Output: 33 | ```go 34 | "Hello Mercury" 35 | "Hello Venus" 36 | "Hello Earth" 37 | "Hello Mars" 38 | "Hello Jupiter" 39 | "Hello Saturn" 40 | "Hello Uranus" 41 | "Hello Neptune" 42 | "Hello Pluto" 43 | ``` 44 | 45 | ### Filter 46 | Filtering a for even numbers only: 47 | 48 | ```go 49 | numbers := []interface{}{ 50 | 1, 51 | 3, 52 | 5, 53 | 6, 54 | 7, 55 | 8, 56 | 9, 57 | 10, 58 | } 59 | 60 | coll := collections.NewFromSlice(numbers) 61 | coll = coll.Filter(func(v interface{}) bool { 62 | return v.(int) % 2 == 0 63 | }) 64 | ``` 65 | 66 | Output: 67 | ```go 68 | 6, 69 | 8, 70 | 10, 71 | ``` 72 | 73 | ### Reduce 74 | Reducing for minimum number: 75 | 76 | ```go 77 | numbers := []interface{}{ 78 | 1, 79 | 3, 80 | 5, 81 | 6, 82 | 7, 83 | 8, 84 | 9, 85 | 10, 86 | } 87 | 88 | coll := collections.NewFromSlice(numbers) 89 | min := collections.Reduce(0, func(a, b interface{}) interface{} { 90 | if a < b { return a } else { return b } 91 | }) 92 | ``` 93 | 94 | Output: 95 | ```go 96 | 1 97 | ``` 98 | 99 | A complete example 100 | ------------------ 101 | The following example assumes a list of ages and returns the average of the ages greater or equal to 21. 102 | 103 | ```go 104 | package main 105 | 106 | import ( 107 | "github.com/alediaferia/go-collections" 108 | "fmt" 109 | ) 110 | 111 | func main() { 112 | ages := []interface{}{ 113 | 3, 114 | 4, 115 | 8, 116 | 17, 117 | 22, 118 | 28, 119 | 34, 120 | 65, 121 | 32, 122 | 24, 123 | } 124 | 125 | coll := collections.NewFromSlice(ages) 126 | count := 0 127 | fmt.Printf("The average age of all the adult people at the party is: %d\n", coll.Filter(func(v interface{}) bool { 128 | return v.(int) >= 21 129 | }).Map(func(v interface{}) interface{} { 130 | count += 1 131 | return v 132 | }).Reduce(0, func(a, b interface{}) interface{} { 133 | return a.(int) + b.(int) 134 | }).(int) / count) 135 | } 136 | ``` 137 | 138 | License 139 | ------- 140 | The code in this codebase is provided as-is as per the MIT License included [here](LICENSE). 141 | 142 | Copyright (c) Alessandro Diaferia 2015 -------------------------------------------------------------------------------- /collections.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | -------------------------------------------------------------------------------- /interfaces.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | type InterfacesMapper func (value interface{}) (interface{}) 4 | type InterfacesFilter func (value interface{}) (bool) 5 | type InterfacesReducer func (a, b interface{}) (interface{}) 6 | 7 | type Interfaces struct { 8 | values []interface{} 9 | } 10 | 11 | func NewFromSlice(values []interface{}) (*Interfaces) { 12 | this := new(Interfaces) 13 | this.values = values 14 | return this 15 | } 16 | 17 | func (this *Interfaces) Map(mapper InterfacesMapper) (*Interfaces) { 18 | new_ := make([]interface{}, 0, len(this.values)) 19 | for _, v := range this.values { 20 | new_ = append(new_, mapper(v)) 21 | } 22 | 23 | return &Interfaces{ values: new_ } 24 | } 25 | 26 | func (this *Interfaces) Filter(filter InterfacesFilter) (*Interfaces) { 27 | new_ := make([]interface{}, 0, len(this.values)) 28 | for _, v := range this.values { 29 | if filter(v) { 30 | new_ = append(new_, v) 31 | } 32 | } 33 | return &Interfaces{ values: new_ } 34 | } 35 | 36 | func (this *Interfaces) Reduce(identity interface{}, reducer InterfacesReducer) (interface{}) { 37 | res := identity 38 | for _, v := range this.values { 39 | res = reducer(res, v) 40 | } 41 | 42 | return res 43 | } 44 | -------------------------------------------------------------------------------- /interfaces_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | import ( 3 | "testing" 4 | "fmt" 5 | ) 6 | 7 | type interface_map_test struct { 8 | source, destination []interface{} 9 | mapper InterfacesMapper 10 | } 11 | 12 | var interface_map_tests = []interface_map_test{ 13 | { 14 | []interface{}{ "a", "aaa", "abc" }, 15 | []interface{}{ "pref_a", "pref_aaa", "pref_abc" }, 16 | func (v interface{}) (interface{}) { return fmt.Sprintf("pref_%s", v.(string)) }, 17 | }, 18 | } 19 | 20 | func TestInterfacesMap(t *testing.T) { 21 | for _, v := range interface_map_tests { 22 | interfaces := NewFromSlice(v.source) 23 | out := interfaces.Map(v.mapper) 24 | if len(out.values) != len(v.destination) { 25 | t.Fatalf("Expected output length %d, got %d", len(v.destination), len(out.values)) 26 | } 27 | for i := 0; i < len(v.destination); i++ { 28 | found := false 29 | for j := 0; j < len(out.values); j++ { 30 | if out.values[j] == v.destination[i] { 31 | found = true 32 | break 33 | } 34 | } 35 | if !found { 36 | t.Errorf("Could not find item %v into output collection", v.destination[i]) 37 | } 38 | } 39 | } 40 | } 41 | 42 | type interface_filter_test struct { 43 | source, destination []interface{} 44 | filter InterfacesFilter 45 | } 46 | 47 | var interface_filter_tests = []interface_filter_test{ 48 | { 49 | []interface{}{ 1,5,6,8,9,12,23,45,67,78 }, 50 | []interface{}{ 6, 8, 12, 78}, 51 | func (v interface{}) (bool) { return (v.(int) % 2) == 0 }, 52 | }, 53 | } 54 | 55 | func TestInterfacesFilter(t* testing.T) { 56 | for _, v := range interface_filter_tests { 57 | interfaces := NewFromSlice(v.source) 58 | out := interfaces.Filter(v.filter) 59 | if len(out.values) != len(v.destination) { 60 | t.Fatalf("Expected output length %d, got %d", len(v.destination), len(out.values)) 61 | } 62 | for i := 0; i < len(v.destination); i++ { 63 | found := false 64 | for j := 0; j < len(out.values); j++ { 65 | if out.values[j] == v.destination[i] { 66 | found = true 67 | break 68 | } 69 | } 70 | if !found { 71 | t.Errorf("Could not find item %v into output collection", v.destination[i]) 72 | } 73 | } 74 | } 75 | } 76 | 77 | type interface_reduce_test struct { 78 | source []interface{} 79 | destination, identity interface{} 80 | reducer InterfacesReducer 81 | } 82 | 83 | var interface_reduce_tests = []interface_reduce_test{ 84 | { 85 | []interface{}{5,2,34,56,12,39,47,54,34,4}, 86 | 2, 87 | 5, 88 | func (a, b interface{}) interface{} { if a.(int) < b.(int) { return a } else { return b } }, 89 | }, 90 | { 91 | []interface{}{5,2,34,56,12,39,47,54,34,4}, 92 | 56, 93 | 5, 94 | func (a, b interface{}) interface{} { if a.(int) > b.(int) { return a } else { return b } }, 95 | }, 96 | } 97 | 98 | func TestInterfaceReduce(t *testing.T) { 99 | for _, v := range interface_reduce_tests { 100 | interfaces := NewFromSlice(v.source) 101 | out := interfaces.Reduce(v.identity, v.reducer) 102 | if out != v.destination { 103 | t.Errorf("Got %v, expected %v", out, v.destination) 104 | } 105 | } 106 | } 107 | --------------------------------------------------------------------------------