Gophers is an awesome collections library for Go offering tons of utilities right out of the box.
Collection Types:
- Sequence : An ordered collection wrapping a Go slice. Great for fast random access.
- ComparableSequence : A Sequence of comparable elements. Offers extra functionality.
- List : An ordered collection wrapping a linked list. Great for fast insertion, removal, and implementing stacks and queues.
- ComparableList : A List of comparable elements. Offers extra functionality.
- Set : A hash set of unique elements.
Here's a few examples of what you can do:
go get github.com/charbz/gophers
import (
"github.com/charbz/gophers/list"
)
type Foo struct {
a int
b string
}
foos := list.NewList([]Foo{
{a: 1, b: "one"},
{a: 2, b: "two"},
{a: 3, b: "three"},
{a: 4, b: "four"},
{a: 5, b: "five"},
})
foos.Filter(func(f Foo) bool { return f.a%2 == 0 })
// List[Foo] {{2 two} {4 four}}
foos.Reject(func(f Foo) bool { return f.a%2 == 0 })
// List[Foo] {{1 one} {3 three} {5 five}}
foos.Find(func(f Foo) bool { return f.a == 3 })
// {a: 3, b: "three"}
foos.Partition(func(f Foo) bool { return len(f.b) == 3 })
// List[Foo] {{1 one} {2 two}} , List[Foo] {{3 three} {4 four} {5 five}}
foos.SplitAt(3)
// List[Foo] {{1 one} {2 two} {3 three} {4 four}} , List[Foo] {{5 five}}
foos.Count(func(f Foo) bool { return f.a < 3 })
// 2
bars := foos.Concat(list.NewList([]Foo{{a: 1, b: "one"}, {a: 2, b: "two"}}))
// List[Foo] {{1 one} {2 two} {3 three} {4 four} {5 five} {1 one} {2 two}}
bars.Distinct(func(i Foo, j Foo) bool { return i.a == j.a })
// List[Foo] {{1 one} {2 two} {3 three} {4 four} {5 five}}
foos.Apply(
func(f Foo) Foo {
f.a *= 2
f.b += " * two"
return f
}
)
// List[Foo] {{2 one * two} {4 two * two} {6 three * two} {8 four * two} {10 five * two}}
Comparable collections are collections with elements that can be compared to each other. They offer all the same functionality as an ordered collection but provide additional convenience methods.
import (
"github.com/charbz/gophers/list"
)
nums := list.NewComparableList([]int{1, 1, 2, 2, 2, 3, 4, 5})
nums.Max() // 5
nums.Min() // 1
nums.Sum() // 20
nums.Distinct() // List[int] {1,2,3,4,5}
nums.Reverse() // List[int] {5,4,3,2,2,1,1}
nums.SplitAt(3) // List[int] {1,1,2,2}, List[int] {2,3,4,5}
nums.Take(3) // List[int] {1,1,2}
nums.TakeRight(3) // List[int] {3,4,5}
nums.Drop(3) // List[int] {2,2,3,4,5}
nums.DropRight(3) // List[int] {1,1,2,2,2}
nums.DropWhile(
func(i int) bool { return i < 3 }, // List[int] {3,4,5}
)
nums.Diff(
list.NewComparableList([]int{1, 2, 3}), // List[int] {4,5}
)
nums.Count(
func(i int) bool { return i > 2 }, // 3
)
Sets are collections of unique elements. They offer all the same functionality as an unordered collection but provide additional methods for set operations.
import (
"github.com/charbz/gophers/set"
)
setA := set.NewSet([]string{"A", "B", "C", "A", "C", "A"}) // Set[string] {"A", "B", "C"}
setB := set.NewSet([]string{"A", "B", "D"})
setA.Intersection(setB) // Set[string] {"A", "B"}
setA.Union(setB) // Set[string] {"A", "B", "C", "D"}
setA.Diff(setB) // Set[string] {"C"}
setA.Apply(
func(s string) string {
s += "!"
return s
}, // Set[string] {"A!", "B!", "C!"}
)
You can use package functions such as Map, Reduce, GroupBy, and many more on any concrete collection type.
import (
"github.com/charbz/gophers/collection"
"github.com/charbz/gophers/list"
"github.com/charbz/gophers/sequence"
)
foos := sequence.NewSequence([]Foo{
{a: 1, b: "one"},
{a: 2, b: "two"},
{a: 3, b: "three"},
{a: 4, b: "four"},
{a: 5, b: "five"},
})
collection.Map(foos, func(f Foo) string { return f.b }) // ["one", "two", "three", "four", "five"]
collection.Reduce(foos, func(acc string, f Foo) string { return acc + f.b }, "") // "onetwothreefourfive"
collection.Reduce(foos, func(acc int, f Foo) int { return acc + f.a }, 0) // 15
collection.GroupBy(foos, func(f Foo) int { return f.a % 2 }) // Map[int][]Foo { 0: [{2 two}, {4 four}], 1: [{1 one}, {3 three}, {5 five}]}
Note: Given that methods cannot define new type parameters in Go, any function that produces a new type, for example Map(List[T], func(T) K) -> List[K]
,
must be called as a function similar to the examples above and cannot be made into a method of the collection type i.e. List[T].Map(func(T) K) -> List[K]
.
This is a minor inconvenience as it breaks the consistency of the API but is a limitation of the language.
All collections implement methods that return iterators over the result as opposed to returning the result itself.
This is useful when combined with the for ... range
syntax to iterate over the result.
import (
"github.com/charbz/gophers/list"
)
A := list.NewComparableList([]int{1, 2, 2, 3})
B := list.NewComparableList([]int{4, 5, 2})
for i := range A.Filtered(func(v int) bool { return v%2 == 0 }) {
fmt.Printf("filtered %v\n", i)
}
// filtered 2
// filtered 2
for i := range A.Rejected(func(v int) bool { return v%2 == 0 }) {
fmt.Printf("rejected %v\n", i)
}
// rejected 1
// rejected 3
for i := range A.Distincted() {
fmt.Printf("distincted %v\n", i)
}
// distincted 1
// distincted 2
// distincted 3
for i := range A.Concatenated(B) {
fmt.Printf("concatenated %v\n", i)
}
// concatenated 1
// concatenated 2
// concatenated 2
// concatenated 3
// concatenated 4
// concatenated 5
// concatenated 2
for i := range A.Diffed(B) {
fmt.Printf("diffed %v\n", i)
}
// diffed 1
// diffed 3
for i := range A.Intersected(B) {
fmt.Printf("intersected %v\n", i)
}
// intersected 2
// intersected 2 // (A is not a set)
Add(element)
- Append element to sequenceAll()
- Get iterator over all elementsAt(index)
- Get element at indexApply(function)
- Apply function to each element (mutates the original collection)Backward()
- Get reverse iterator over elementsClone()
- Create shallow copy of sequenceConcat(sequences...)
- Concatenates any passed sequencesConcatenated(sequence)
- Get iterator over concatenated sequenceContains(predicate)
- Test if any element matches predicateCorresponds(sequence, function)
- Test element-wise correspondenceCount(predicate)
- Count elements matching predicateDequeue()
- Remove and return first elementDiff(sequence, function)
- Get elements in first sequence but not in secondDiffed(sequence, function)
- Get iterator over elements in first sequence but not in secondDistinct(function)
- Get unique elements using equality functionDistincted()
- Get unique elements using equality comparisonDrop(n)
- Drop first n elementsDropRight(n)
- Drop last n elementsDropWhile(predicate)
- Drop elements while predicate is trueEnqueue(element)
- Add element to endEquals(sequence, function)
- Test sequence equality using functionExists(predicate)
- Test if any element matches predicateFilter(predicate)
- Filter elements based on predicateFilterNot(predicate)
- Inverse filter operationFind(predicate)
- Find first matching elementFindLast(predicate)
- Find last matching elementForAll(predicate)
- Test if predicate holds for all elementsHead()
- Get first elementInit()
- Get all elements except lastIntersect(sequence, function)
- Get elements present in both sequencesIntersected(sequence, function)
- Get iterator over elements present in both sequencesIsEmpty()
- Test if sequence is emptyLast()
- Get last elementLength()
- Get number of elementsNew(slices...)
- Create new sequenceNewOrdered(slices...)
- Create new ordered sequenceNonEmpty()
- Test if sequence is not emptyPartition(predicate)
- Split sequence based on predicatePop()
- Remove and return last elementPush(element)
- Add element to endRandom()
- Get random elementReverse()
- Reverse order of elementsReject(predicate)
- Inverse filter operationRejected(predicate)
- Get iterator over elements rejected by predicateSlice(start, end)
- Get subsequence from start to endSplitAt(n)
- Split sequence at index nString()
- Get string representationTake(n)
- Get first n elementsTakeRight(n)
- Get last n elementsTail()
- Get all elements except firstToSlice()
- Convert to Go sliceValues()
- Get iterator over values
Inherits all operations from Sequence, but with the following additional operations:
Contains(element)
- Test if sequence contains elementDistinct()
- Get unique elements using equality comparisonDiff(sequence)
- Get elements in first sequence but not in secondEquals(sequence)
- Test sequence equality using equality comparisonExists(element)
- Test if sequence contains elementIndexOf(element)
- Get index of first occurrence of elementLastIndexOf(element)
- Get index of last occurrence of elementMax()
- Get maximum elementMin()
- Get minimum elementSum()
- Get sum of all elements
Add(element)
- Add element to endAll()
- Get iterator over index/value pairsApply(function)
- Apply function to each elementAt(index)
- Get element at indexBackward()
- Get reverse iterator over index/value pairsClone()
- Create shallow copyConcat(lists...)
- Concatenate multiple listsConcatenated(list)
- Get iterator over concatenated listContains(predicate)
- Test if any element matches predicateCorresponds(list, function)
- Test element-wise correspondenceCount(predicate)
- Count elements matching predicateDequeue()
- Remove and return first elementDiff(list, function)
- Get elements in first list but not in secondDiffed(list, function)
- Get iterator over elements in first list but not in secondDistinct(function)
- Get unique elements using equality functionDistincted()
- Get unique elements using equality comparisonDrop(n)
- Drop first n elementsDropRight(n)
- Drop last n elementsDropWhile(predicate)
- Drop elements while predicate is trueEnqueue(element)
- Add element to endEquals(list, function)
- Test list equality using functionExists(predicate)
- Test if any element matches predicateFilter(predicate)
- Filter elements based on predicateFilterNot(predicate)
- Inverse filter operationFind(predicate)
- Find first matching elementFindLast(predicate)
- Find last matching elementForAll(predicate)
- Test if predicate holds for all elementsHead()
- Get first elementInit()
- Get all elements except lastIntersect(list, function)
- Get elements present in both listsIntersected(list, function)
- Get iterator over elements present in both listsIsEmpty()
- Test if list is emptyLast()
- Get last elementLength()
- Get number of elementsNew(slices...)
- Create new listNewOrdered(slices...)
- Create new ordered listNonEmpty()
- Test if list is not emptyPartition(predicate)
- Split list based on predicatePop()
- Remove and return last elementPush(element)
- Add element to endRandom()
- Get random elementReverse()
- Reverse order of elementsReject(predicate)
- Inverse filter operationRejected(predicate)
- Get iterator over elements rejected by predicateSlice(start, end)
- Get sublist from start to endSplitAt(n)
- Split list at index nString()
- Get string representationTake(n)
- Get first n elementsTakeRight(n)
- Get last n elementsTail()
- Get all elements except firstToSlice()
- Convert to Go sliceValues()
- Get iterator over values
Inherits all operations from List, but with the following additional operations:
Contains(value)
- Test if list contains valueDistinct()
- Get unique elementsDiff(list)
- Get elements in first list but not in secondExists(value)
- Test if list contains value (alias for Contains)Equals(list)
- Test list equalityIndexOf(value)
- Get index of first occurrence of valueLastIndexOf(value)
- Get index of last occurrence of valueMax()
- Get maximum elementMin()
- Get minimum elementSum()
- Get sum of all elements
Add(element)
- Add element to setApply(function)
- Apply function to each elementClone()
- Create shallow copy of setContains(value)
- Test if set contains valueContainsFunc(predicate)
- Test if set contains element matching predicateCount(predicate)
- Count elements matching predicateDiff(set)
- Get elements in first set but not in secondDiffed(set)
- Get iterator over elements in first set but not in secondEquals(set)
- Test set equalityFilter(predicate)
- Filter elements based on predicateFilterNot(predicate)
- Inverse filter operationForAll(predicate)
- Test if predicate holds for all elementsIntersection(set)
- Get elements present in both setsIntersected(set)
- Get iterator over elements present in both setsIsEmpty()
- Test if set is emptyLength()
- Get number of elementsNew(slices...)
- Create new setNonEmpty()
- Test if set is not emptyPartition(predicate)
- Split set based on predicateRandom()
- Get random elementRemove(element)
- Remove element from setReject(predicate)
- Inverse filter operationRejected(predicate)
- Get iterator over elements rejected by predicateString()
- Get string representationToSlice()
- Convert to Go sliceUnion(set)
- Get elements present in either setUnioned(set)
- Get iterator over elements present in either setValues()
- Get iterator over values
The following package functions can be called on any collection, including Sequence, ComparableSequence, List, ComparableList, and Set.
Count(collection, predicate)
- Count elements matching predicateDiff(collection)
- Get elements in first collection but not in secondDistinct(collection, function)
- Get unique elementsFilter(collection, predicate)
- Filter elements based on predicateFilterNot(collection, predicate)
- Inverse filter operationForAll(collection, predicate)
- Test if predicate holds for all elementsGroupBy(collection, function)
- Group elements by key functionIntersect(collection1, collection2)
- Get elements present in both collectionsMap(collection, function)
- Transform elements using functionMaxBy(collection, function)
- Get maximum element by comparison functionMinBy(collection, function)
- Get minimum element by comparison functionPartition(collection, predicate)
- Split collection based on predicateReduce(collection, function, initial)
- Reduce collection to single value
The package functions below can be called on ordered collections (Sequence, ComparableSequence, List, and ComparableList):
Corresponds(collection1, collection2, function)
- test whether values in collection1 map into values in collection2 by the given functionDrop(collection, n)
- Drop first n elementsDropRight(collection, n)
- Drop last n elementsDropWhile(collection, predicate)
- Drop elements while predicate is trueFind(collection, predicate)
- returns the index and value of the first element matching predicateFindLast(collection, predicate)
- returns the index and value of the last element matching predicateHead(collection)
- returns the first element in a collectionInit(collection)
- returns all elements excluding the last oneLast(collection)
- Get last elementReduceRight(collection, function, initial)
- Right-to-left reductionReverse(collection)
- Reverse order of elementsReverseMap(collection, function)
- Map elements in reverse orderSplitAt(collection, n)
- Split collection at index nTail(collection)
- Get all elements except firstTake(collection, n)
- Get first n elementsTakeRight(collection, n)
- Get last n elements
The following package functions return an iterator for the result:
Concatenated(collection1, collection2)
- Get iterator over concatenated collectionDiffed(collection1, collection2, function)
- Get iterator over elements in first collection but not in secondIntersected(collection1, collection2, function)
- Get iterator over elements present in both collectionsMapped(collection, function)
- Get iterator over elements transformed by functionRejected(collection, predicate)
- Get iterator over elements rejected by predicate
Contributions are welcome! Feel free to submit a Pull Request.
If you have any ideas for new features or improvements, or would like to chat,
Feel free to reach out on Discord: Gophers Project or on Reddit: r/gopherslib