Gollections is set of functions for slices, maps, iter.Seq, iter.Seq2 and additional implementations of data structures such as ordered map or set aimed to reduce boilerplate code.
Supports Go version 1.24.
For example, it’s need to group some users by their role names converted to lowercase:
var users = []User{
{name: "Bob", age: 26, roles: []Role{{"Admin"}, {"manager"}}},
{name: "Alice", age: 35, roles: []Role{{"Manager"}}},
{name: "Tom", age: 18},
}
You can make clear code, extensive, but without dependencies:
var namesByRole = map[string][]string{}
add := func(role string, u User) {
namesByRole[role] = append(namesByRole[role], u.Name())
}
for _, u := range users {
if roles := u.Roles(); len(roles) == 0 {
add("", u)
} else {
for _, r := range roles {
add(strings.ToLower(r.Name()), u)
}
}
}
//map[:[Tom] admin:[Bob] manager:[Bob Alice]]
Or you can write more compact code using the collections API, like:
import (
"github.com/m4gshm/gollections/slice/convert"
"github.com/m4gshm/gollections/slice/group"
)
var namesByRole = group.ByMultipleKeys(users, func(u User) []string {
return convert.AndConvert(u.Roles(), Role.Name, strings.ToLower)
}, User.Name)
// map[:[Tom] admin:[Bob] manager:[Bob Alice]]
go get -u github.com/m4gshm/gollections
data, err := slice.Conv(slice.Of("1", "2", "3", "4", "_", "6"), strconv.Atoi)
//[1 2 3 4], invalid syntax
even := func(i int) bool { return i%2 == 0 }
result := slice.Reduce(slice.Convert(slice.Filter(data, even), strconv.Itoa), op.Sum) //"24"
In the example is used only small set of slice functions as slice.Filter, slice.Conv slice.Convert, and slice.Reduce. More you can look in the slice package.
result := sum.Of(filter.AndConvert(data, even, strconv.Itoa))
This is a shorter version of the previous example that used short aliases sum.Of and filter.AndConvert. More shortcuts you can find by exploring slices subpackages.
Be careful when use several slice functions subsequently like
slice.Filter(slice.Convert(…​))
. This can lead to unnecessary RAM
consumption. Consider seq instead of slice API.
var s = slice.Of(1, 3, -1, 2, 0) //[]int{1, 3, -1, 2, 0}
import "github.com/m4gshm/gollections/slice/range_"
var increasing = range_.Of(-1, 3) //[]int{-1, 0, 1, 2}
var decreasing = range_.Of('e', 'a') //[]rune{'e', 'd', 'c', 'b'}
var nothing = range_.Of(1, 1) //nil
var increasing = range_.Closed(-1, 3) //[]int{-1, 0, 1, 2, 3}
var decreasing = range_.Closed('e', 'a') //[]rune{'e', 'd', 'c', 'b', 'a'}
var one = range_.Closed(1, 1) //[]int{1}
// sorting in-place API
import "github.com/m4gshm/gollections/slice/sort"
var ascendingSorted = sort.Asc([]int{1, 3, -1, 2, 0}) //[]int{-1, 0, 1, 2, 3}
var descendingSorted = sort.Desc([]int{1, 3, -1, 2, 0}) //[]int{3, 2, 1, 0, -1}
// sorting copied slice API does not change the original slice
import "github.com/m4gshm/gollections/slice/clone/sort"
// see the User structure above
var users = []User{
{name: "Bob", age: 26},
{name: "Alice", age: 35},
{name: "Tom", age: 18},
{name: "Chris", age: 41},
}
var byName = sort.By(users, User.Name)
//[{Alice 35 []} {Bob 26 []} {Chris 41 []} {Tom 18 []}]
var byAgeReverse = sort.DescBy(users, User.Age)
//[{Chris 41 []} {Alice 35 []} {Bob 26 []} {Tom 18 []}]
import (
"github.com/m4gshm/gollections/convert/as"
"github.com/m4gshm/gollections/expr/use"
"github.com/m4gshm/gollections/slice/group"
)
var ageGroups map[string][]User = group.Of(users, func(u User) string {
return use.If(u.age <= 20, "<=20").If(u.age <= 30, "<=30").Else(">30")
}, as.Is)
//map[<=20:[{Tom 18 []}] <=30:[{Bob 26 []}] >30:[{Alice 35 []} {Chris 41 []}]]
import (
"github.com/m4gshm/gollections/convert/as"
"github.com/m4gshm/gollections/expr/use"
"github.com/m4gshm/gollections/slice/group"
)
var order, ageGroups = group.Order(users, func(u User) string {
return use.If(u.age <= 20, "<=20").If(u.age <= 30, "<=30").Else(">30")
}, as.Is)
//order [<=30 >30 <=20]
//ageGroups map[<=20:[{Tom 18 []}] <=30:[{Bob 26 []}] >30:[{Alice 35 []} {Chris 41 []}]]
import (
"github.com/m4gshm/gollections/slice/convert"
"github.com/m4gshm/gollections/slice/group"
)
var namesByRole = group.ByMultipleKeys(users, func(u User) []string {
return convert.AndConvert(u.Roles(), Role.Name, strings.ToLower)
}, User.Name)
// map[:[Tom] admin:[Bob] manager:[Bob Alice]]
import "github.com/m4gshm/gollections/slice"
var agePerGroup map[string]int = slice.Map(users, User.Name, User.Age)
//"map[Alice:35 Bob:26 Chris:41 Tom:18]"
import "github.com/m4gshm/gollections/slice"
var names, agePerName = slice.MapOrder(users, User.Name, User.Age)
//"[Bob Alice Tom Chris]"
//"map[Alice:35 Bob:26 Chris:41 Tom:18]"
import (
"github.com/m4gshm/gollections/map_/resolv"
"github.com/m4gshm/gollections/op"
"github.com/m4gshm/gollections/slice"
)
var ageGroupedSortedNames map[string][]string
ageGroupedSortedNames = slice.MapResolv(users, func(u User) string {
return op.IfElse(u.age <= 30, "<=30", ">30")
}, User.Name, resolv.SortedSlice)
//map[<=30:[Bob Tom] >30:[Alice Chris]]
import "github.com/m4gshm/gollections/op/sum"
var sum = sum.Of(1, 2, 3, 4, 5, 6) //21
var sum = slice.Reduce([]int{1, 2, 3, 4, 5, 6}, func(i1, i2 int) int { return i1 + i2 })
//21
import (
"github.com/m4gshm/gollections/op"
"github.com/m4gshm/gollections/slice"
)
var sum = slice.Accum(100, slice.Of(1, 2, 3, 4, 5, 6), op.Sum)
//121
import (
"github.com/m4gshm/gollections/predicate/more"
"github.com/m4gshm/gollections/slice"
)
result, ok := slice.First([]int{1, 3, 5, 7, 9, 11}, more.Than(5)) //7, true
import (
"github.com/m4gshm/gollections/slice"
)
result, ok := slice.Head([]int{1, 3, 5, 7, 9, 11}) //1, true
import (
"github.com/m4gshm/gollections/slice"
)
result := slice.Top(3, []int{1, 3, 5, 7, 9, 11}) //[]int{1, 3, 5}
import (
"github.com/m4gshm/gollections/predicate/less"
"github.com/m4gshm/gollections/slice"
)
result, ok := slice.Last([]int{1, 3, 5, 7, 9, 11}, less.Than(9)) //7, true
import (
"github.com/m4gshm/gollections/slice"
)
result, ok := slice.Tail([]int{1, 3, 5, 7, 9, 11}) //11, true
var s []string = slice.Convert([]int{1, 3, 5, 7, 9, 11}, strconv.Itoa)
//[]string{"1", "3", "5", "7", "9", "11"}
result, err := slice.Conv(slice.Of("1", "3", "5", "_7", "9", "11"), strconv.Atoi)
//[]int{1, 3, 5}, ErrSyntax
import (
"github.com/m4gshm/gollections/predicate/exclude"
"github.com/m4gshm/gollections/predicate/one"
"github.com/m4gshm/gollections/slice"
)
var f1 = slice.Filter([]int{1, 3, 5, 7, 9, 11}, one.Of(1, 7).Or(one.Of(11))) //[]int{1, 7, 11}
var f2 = slice.Filter([]int{1, 3, 5, 7, 9, 11}, exclude.All(1, 7, 11)) //[]int{3, 5, 9}
import (
"github.com/m4gshm/gollections/convert/as"
"github.com/m4gshm/gollections/slice"
)
var i []int = slice.Flat([][]int{{1, 2, 3}, {4}, {5, 6}}, as.Is)
//[]int{1, 2, 3, 4, 5, 6}
-
convert.AndReduce, conv.AndReduce
-
convert.AndFilter
-
filter.AndConvert
These functions combine converters, filters and reducers.
import "github.com/m4gshm/gollections/map_/clone"
var bob = map[string]string{"name": "Bob"}
var tom = map[string]string{"name": "Tom"}
var employers = map[string]map[string]string{
"devops": bob,
"jun": tom,
}
copy := clone.Of(employers)
delete(copy, "jun")
bob["name"] = "Superbob"
fmt.Printf("%v\n", employers) //map[devops:map[name:Superbob] jun:map[name:Tom]]
fmt.Printf("%v\n", copy) //map[devops:map[name:Superbob]]
import "github.com/m4gshm/gollections/map_/clone"
var bob = map[string]string{"name": "Bob"}
var tom = map[string]string{"name": "Tom"}
var employers = map[string]map[string]string{
"devops": bob,
"jun": tom,
}
copy := clone.Deep(employers, func(employer map[string]string) map[string]string {
return clone.Of(employer)
})
delete(copy, "jun")
bob["name"] = "Superbob"
fmt.Printf("%v\n", employers) //map[devops:map[name:Superbob] jun:map[name:Tom]]
fmt.Printf("%v\n", copy) //map[devops:map[name:Bob]]
var users = map_.Slice(employers, func(title string, employer map[string]string) User {
return User{name: employer["name"], roles: []Role{{name: title}}}
})
//[{name:Bob age:0 roles:[{name:devops}]} {name:Tom age:0 roles:[{name:jun}]}]
var employers = map[string]map[string]string{
"devops": {"name": "Bob"},
"jun": {"name": "Tom"},
}
keys := map_.Keys(employers) //[devops jun]
values := map_.Values(employers) //[map[name:Bob] map[name:Tom]]
var keys = map_.ConvertKeys(employers, func(title string) string {
return string([]rune(title)[0])
})
//map[d:map[name:Bob] j:map[name:Tom]]
var vals = map_.ConvertValues(employers, func(employer map[string]string) string {
return employer["name"]
})
//map[devops:Bob jun:Tom]
var all = map_.Convert(employers, func(title string, employer map[string]string) (string, string) {
return string([]rune(title)[0]), employer["name"]
})
//map[d:Bob j:Tom]
var all, err = map_.Conv(employers, func(title string, employer map[string]string) (string, string, error) {
return string([]rune(title)[0]), employer["name"], nil
})
//map[d:Bob j:Tom], nil
API extends rangefunc iterator types iter.Seq[V]
, iter.Seq2[K,V]
with utility functions kit.
even := func(i int) bool { return i%2 == 0 }
strSeq := seq.Convert(seq.Filter(seq.Of(1, 2, 3, 4), even), strconv.Itoa)
// iterate over sequence
for s := range strSeq {
fmt.Println(s)
}
// or reduce
var oneString string = seq.Sum(strSeq) // 24
// or collect
var strings []string = seq.Slice(strSeq) //[2 4]
or
intSeq := seq.Conv(seq.Of("1", "2", "3", "ddd4", "5"), strconv.Atoi)
ints, err := seqe.Slice(intSeq) //[1 2 3], invalid syntax
import(
"github.com/m4gshm/gollections/seq"
"github.com/m4gshm/gollections/seq2"
)
var (
ints seq.Seq[int] = seq.Of(1, 2, 3)
pairs seq.Seq2[string, int] = seq2.OfMap(map[string]int{
"first": 1,
"second": 2,
"third": 3,
})
)
assert.Equal(t, []int{3, 2, 1}, sort.Desc(pairs.Values().Slice()))
import(
"database/sql"
"log"
"github.com/m4gshm/gollections/seq2"
)
var rows sql.Rows = selectUsers()
getUser := func(u *User) error { return rows.Scan(&u.name, &u.age) }
rowSeq := seqe.OfNext(rows.Next, getUser)
usersByAge, err := seqe.Group(rowSeq, User.Age, as.Is)
instead of:
import(
"database/sql"
"log"
)
var rows sql.Rows = selectUsers()
var usersByAge = map[int][]User{}
var err error
for rows.Next() {
var u User
if err = rows.Scan(&u.name, &u.age); err != nil {
break
}
usersByAge[u.age] = append(usersByAge[u.age], u)
}
import(
"github.com/m4gshm/gollections/seq"
)
var numbers []int
for n := range seq.Range(5, -2) {
numbers = append(numbers, n)
}
//[]int{5, 4, 3, 2, 1, 0, -1}
import(
"github.com/m4gshm/gollections/seq"
)
var numbers, factorials []int
next := func(i, prev int) (int, bool) {
return i * prev, i <= 5
}
for i, n := range seq2.Series(1, next) {
numbers = append(numbers, i)
factorials = append(factorials, n)
}
//[]int{0, 1, 2, 3, 4, 5}
//[]int{1, 1, 2, 6, 24, 120}
filter := func(u User) bool { return u.age <= 30 }
less30Names := seq.Convert(seq.Of(users...).Filter(filter), User.Name)
names := seq.Slice(less30Names)
//[Bob Tom]
//or
names = less30Names.Slice()
//[Bob Tom]
import (
"iter"
"github.com/m4gshm/gollections/expr/use"
"github.com/m4gshm/gollections/seq"
"github.com/m4gshm/gollections/seq2"
"github.com/m4gshm/gollections/slice"
"github.com/m4gshm/gollections/slice/sort"
)
var users seq.Seq[User] = seq.Of(users...)
var groups seq.Seq2[string, User] = seq.ToSeq2(users, func(u User) (string, User) {
return use.If(u.age <= 20, "<=20").If(u.age <= 30, "<=30").Else(">30"), u
})
var ageGroups map[string][]User = seq2.Group(groups)
//map[<=20:[{Tom 18 []}] <=30:[{Bob 26 []}] >30:[{Alice 35 []} {Chris 41 []}]]
assert.Equal(t, slice.Of("Alice", "Chris"), sort.Asc(slice.Convert(ageGroups[">30"], User.Name)))
adder := func(i1, i2 int) int { return i1 + i2 }
var sum = seq.Reduce(seq.Of(1, 2, 3, 4, 5, 6), adder)
//21
//or
sum = seq.Of(1, 2, 3, 4, 5, 6).Reduce(adder)
//21
adder := func(i1, i2 int) int { return i1 + i2 }
sum, ok := seq.ReduceOK(seq.Of(1, 2, 3, 4, 5, 6), adder)
//21, true
//or
emptyLoop := seq.Of[int]()
sum, ok = emptyLoop.ReduceOK(adder)
//0, false
import (
"github.com/m4gshm/gollections/predicate/more"
"github.com/m4gshm/gollections/seq"
)
result, ok := seq.First(seq.Of(1, 3, 5, 7, 9, 11), more.Than(5)) //7, true
//or
result, ok = seq.Of(1, 3, 5, 7, 9, 11).First(more.Than(5)) //7, true
import (
"github.com/m4gshm/gollections/seq"
)
result, ok := seq.Head(seq.Of(1, 3, 5, 7, 9, 11)) //1, true
//or
result, ok = seq.Of(1, 3, 5, 7, 9, 11).Head() //1, true
var result []string
for s := range seq.Convert(seq.Of(1, 3, 5, 7, 9, 11), strconv.Itoa) {
result = append(result, s)
}
//[]string{"1", "3", "5", "7", "9", "11"}
var result []int
for i, err := range seq.Conv(seq.Of("1", "3", "5", "_7", "9", "11"), strconv.Atoi) {
if err != nil {
//ErrSyntax
break
}
result = append(result, i)
}
//[]int{1, 3, 5}
import (
"github.com/m4gshm/gollections/seq"
)
var result []int
seq1 := seq.Of(1, 3, 5)
seq2 := seq.Of(7, 9, 11)
for i := range seq.Union(seq1, seq2) {
result = append(result, i)
}
//[]int{1, 3, 5, 7, 9, 11}
import (
"github.com/m4gshm/gollections/predicate/exclude"
"github.com/m4gshm/gollections/predicate/one"
"github.com/m4gshm/gollections/seq"
)
var f1 = seq.Slice(seq.Filter(seq.Of(1, 3, 5, 7, 9, 11), one.Of(1, 7).Or(one.Of(11))))
//[]int{1, 7, 11}
var f2 = seq.Of(1, 3, 5, 7, 9, 11).Filter(exclude.All(1, 7, 11)).Slice()
//[]int{3, 5, 9}
import (
"github.com/m4gshm/gollections/seq"
)
i := seq.Slice(seq.Top(4, seq.Of(1, 3, 5, 7, 9, 11)))
//[]int{1, 3, 5, 7}
//or
i = seq.Of(1, 3, 5, 7, 9, 11).Top(4).Slice()
//[]int{1, 3, 5, 7}
import (
"github.com/m4gshm/gollections/seq"
)
i := seq.Slice(seq.Skip(4, seq.Of(1, 3, 5, 7, 9, 11)))
//[]int{9, 11}
//or
i = seq.Of(1, 3, 5, 7, 9, 11).Skip(4).Slice()
//[]int{9, 11}
import (
"github.com/m4gshm/gollections/convert/as"
"github.com/m4gshm/gollections/seq"
)
twoDimensions := [][]int{{1, 2, 3}, {4}, {5, 6}}
var i []int = seq.Slice(seq.Flat(seq.Of(twoDimensions...), as.Is))
//[]int{1, 2, 3, 4, 5, 6}
Provides implelentations of Vector, Set and Map.
Mutables support content appending, updating and deleting (the ordered
map implementation is not supported delete operations).
Immutables are read-only datasets.
Detailed description of implementations below.
predicate and breakable break/predicate
Provides predicate builder api that used for filtering collection elements.
import (
"github.com/m4gshm/gollections/predicate/where"
"github.com/m4gshm/gollections/slice"
)
bob, _ := slice.First(users, where.Eq(User.Name, "Bob"))
Aimed to evaluate a value using conditions. May cause to make code
shorter by not in all cases.
As example:
import "github.com/m4gshm/gollections/expr/use"
user := User{name: "Bob", surname: "Smith"}
fullName := use.If(len(user.surname) == 0, user.name).If(len(user.name) == 0, user.surname).
ElseGet(func() string { return user.name + " " + user.surname })
//Bob Smith
instead of:
user := User{name: "Bob", surname: "Smith"}
fullName := ""
if len(user.surname) == 0 {
fullName = user.name
} else if len(user.name) == 0 {
fullName = user.surname
} else {
fullName = user.name + " " + user.surname
}
//Bob Smith
import (
"github.com/m4gshm/gollections/expr/first"
"github.com/m4gshm/gollections/predicate/more"
)
result, ok := first.Of(1, 3, 5, 7, 9, 11).By(more.Than(5)) //7, true
import (
"github.com/m4gshm/gollections/expr/last"
"github.com/m4gshm/gollections/predicate/less"
)
result, ok := last.Of(1, 3, 5, 7, 9, 11).By(less.Than(9)) //7, true
Supports write operations (append, delete, replace).
- Vector - the simplest based on built-in slice collection.
_ *mutable.Vector[int] = vector.Of(1, 2, 3)
_ *mutable.Vector[int] = &mutable.Vector[int]{}
- Set - collection of unique items, prevents duplicates.
_ *mutable.Set[int] = set.Of(1, 2, 3)
_ *mutable.Set[int] = &mutable.Set[int]{}
- Map - built-in map wrapper that supports stream functions.
_ *mutable.Map[int, string] = map_.Of(k.V(1, "1"), k.V(2, "2"), k.V(3, "3"))
_ *mutable.Map[int, string] = mutable.NewMapOf(map[int]string{1: "2", 2: "2", 3: "3"})
- OrderedSet - collection of unique items, prevents duplicates, provides iteration in order of addition.
_ *ordered.Set[int] = set.Of(1, 2, 3)
_ *ordered.Set[int] = &ordered.Set[int]{}
- OrderedMap - same as the Map, but supports iteration in the order in which elements are added.
_ *ordered.Map[int, string] = map_.Of(k.V(1, "1"), k.V(2, "2"), k.V(3, "3"))
_ *ordered.Map[int, string] = ordered.NewMapOf(
/*order */ []int{3, 1, 2},
/*uniques*/ map[int]string{1: "2", 2: "2", 3: "3"},
)
The same underlying interfaces but for read-only use cases.
- Using rangefunc
All
like:
uniques := set.Of(1, 2, 3, 4, 5, 6)
for i := range uniques.All {
doOp(i)
}
}
ForEach
method
uniques := set.Of(1, 2, 3, 4, 5, 6)
uniques.ForEach(doOp)