A very light Streams implementation in Golang
To install Streams, use go get:
go get github.com/go-andiamo/streams
To update Streams to the latest version, run:
go get -u github.com/go-andiamo/streams
Stream Interface
Method and description | Returns |
---|---|
AllMatch(p Predicate[T])
if the provided predicate is nil or the stream is empty, always returns false |
bool
|
AnyMatch(p Predicate[T])
if the provided predicate is nil or the stream is empty, always returns false |
bool
|
Append(items ...T)
|
Stream[T]
|
AsSlice()
|
[]T
|
Concat(add Stream[T])
|
Stream[T]
|
Count(p Predicate[T])
If the predicate is nil, returns the count of all elements |
int
|
Difference(other Stream[T], c Comparator[T])
equality of elements is determined using the provided comparator (if the provided comparator is nil, the result is always empty) |
Stream[T]
|
Distinct()
|
Stream[T]
|
Filter(p Predicate[T])
if the provided predicate is nil, all elements in this stream are returned |
Stream[T]
|
FirstMatch(p Predicate[T])
if no elements match the provided predicate, an empty (not present) optional is returned if the provided predicate is nil, the first element in this stream is returned |
*Optional[T]
|
ForEach(c Consumer[T])
the action to be performed is defined by the provided consumer if the provided consumer is nil, nothing is performed |
error
|
Has(v T, c Comparator[T])
equality is determined using the provided comparator if the provided comparator is nil, always returns false |
bool
|
Intersection(other Stream[T], c Comparator[T])
equality of elements is determined using the provided comparator (if the provided comparator is nil, the result is always empty) |
Stream[T]
|
Iterator(ps ...Predicate[T])
the iterator function can be used in for loops, for example next := strm.Iterator() for v, ok := next(); ok; v, ok = next() { fmt.Println(v) } Iterator can also optionally accept varargs of Predicate - which, if specified, are logically OR-ed on each pull to ensure that pulled elements match
|
func() (T, bool)
|
LastMatch(p Predicate[T])
if no elements match the provided predicate, an empty (not present) optional is returned if the provided predicate is nil, the last element in this stream is returned |
*Optional[T]
|
Len()
|
int
|
Limit(maxSize int)
if the maximum size is greater than the length of this stream, all elements are returned |
Stream[T]
|
Max(c Comparator[T])
if the provided comparator is nil or the stream is empty, an empty (not present) optional is returned |
*Optional[T]
|
Min(c Comparator[T])
if the provided comparator is nil or the stream is empty, an empty (not present) optional is returned |
*Optional[T]
|
MinMax(c Comparator[T])
if the provided comparator is nil or the stream is empty, an empty (not present) optional is returned for both |
(*Optional[T], *Optional[T])
|
NoneMatch(p Predicate[T])
if the provided predicate is nil or the stream is empty, always returns true |
bool
|
NthMatch(p Predicate[T], nth int)
if the nth argument is negative, the nth is taken as relative to the last if the provided predicate is nil, any element is taken as matching if no elements match in the specified position, an empty (not present) optional is returned |
*Optional[T]
|
Reverse()
|
Stream[T]
|
Skip(n int)
if the specified n to skip is equal to or greater than the number of elements in this stream, an empty stream is returned |
Stream[T]
|
Slice(start int, count int)
the start is zero based (and less than zero is ignored) if the specified count is negative, items are selected from the start and then backwards by the count |
Stream[T]
|
Sorted(c Comparator[T])
if the provided comparator is nil, the elements are not sorted |
Stream[T]
|
SymmetricDifference(other Stream[T], c Comparator[T])
equality of elements is determined using the provided comparator (if the provided comparator is nil, the result is always empty) |
Stream[T]
|
Union(other Stream[T], c Comparator[T])
equality of elements is determined using the provided comparator (if the provided comparator is nil, the result is always empty) |
Stream[T]
|
Unique(c Comparator[T])
uniqueness is determined using the provided comparator if provided comparator is nil but the value type of elements in this stream are directly mappable (i.e. primitive or non-pointer types) then Distinct is used as the result, otherwise returns an empty stream
|
Stream[T]
|
Constructors | |
Of[T any](values ...T) Stream[T]
|
|
OfSlice[T any](s []T) Stream[T]
Note: Once created, If the slice changes the stream does not |
|
NewStreamableSlice[T any](sl *[]T) Stream[T]
It differs from casting a slice to Streamable in that if the underlying slice changes, so does the Stream |
|
Casting as Streamablesl := []string{"a", "b", "c"} s := Streamable[string](sl)
Stream Note: Once cast, if the slice changes the stream does not |
Comparator Interface
Method and description | Returns |
---|---|
Compare(v1, v2 T)
|
int
|
Greater(v1, v2 T)
|
bool
|
GreaterOrEqual(v1, v2 T)
|
bool
|
Equal(v1, v2 T)
|
bool
|
Less(v1, v2 T)
|
bool
|
LessOrEqual(v1, v2 T)
|
bool
|
NotEqual(v1, v2 T)
|
bool
|
Reversed()
the reversal is against less/greater as well as against equality/non-equality |
Comparator[T]
|
Then(other Comparator[T])
|
Comparator[T]
|
Constructors | |
NewComparator[T any](f ComparatorFunc[T]) Comparator[T]
Comparator from the function providedwhere the comparator function is: type ComparatorFunc[T any] func(v1, v2 T) int which returns: |
Consumer Interface
Method and description | Returns |
---|---|
Accept(v T)
|
error
|
AndThen(after Consumer[T])
multiple consumers can be chained together as one using this method |
Consumer[T]
|
Constructors | |
NewConsumer[T any](f ConsumerFunc[T]) Consumer[T]
Consumer from the function providedwhere the consumer function is: type ConsumerFunc[T any] func(v T) error
|
Predicate Interface
Method and description | Returns |
---|---|
Test(v T)
|
bool
|
And(other Predicate[T])
|
Predicate[T]
|
Or(other Predicate[T])
|
Predicate[T]
|
Negate()
|
Predicate[T]
|
Constructors | |
NewPredicate[T any](f PredicateFunc[T]) Predicate[T]
Predicate from the function providedwhere the predicate function is: type PredicateFunc[T any] func(v T) bool
|
Mapper Interface
Method and description | Returns |
---|---|
Map(in Stream[T])
Stream and produces a Stream of output types
|
(Stream[R], error)
|
Constructors | |
NewMapper[T any, R any](c Converter[T, R]) Mapper[T, R]
Mapper that will use the provided Converter NewMapper panics if a nil Converter is supplied
|
Converter Interface
Method and description | Returns |
---|---|
Convert(v T)
|
(R, error)
|
Constructors | |
NewConverter[T any, R any](f ConverterFunc[T, R]) Converter[T, R]
Converter from the function providedwhere the converter function is: type ConverterFunc[T any, R any] func(v T) (R, error)
|
Reducer Interface
Method and description | Returns |
---|---|
Reduce(s Stream[T])
Stream
|
R
|
Constructors | |
NewReducer[T any, R any](accumulator Accumulator[T, R]) Reducer[T, R]
Reducer that will use the supplied Accumulator NewReducer panics if a nil Accumulator is supplied
|
Accumulator Interface
Method and description | Returns |
---|---|
Apply(t T, r R)
|
R
|
Constructors | |
NewAccumulator[T any, R any](f AccumulatorFunc[T, R]) Accumulator[T, R]
Accumulator from the function providedwhere the accumulator function is: type AccumulatorFunc[T any, R any] func(t T, r R) R
|
Find first match...
package main
import (
. "github.com/go-andiamo/streams"
"strings"
)
func main() {
s := Of("a", "B", "c", "D", "e", "F")
upperPredicate := NewPredicate(func(v string) bool {
return strings.ToUpper(v) == v
})
o := s.FirstMatch(upperPredicate)
o.IfPresentOtherwise(
func(v string) {
println(`Found: "` + v + `"`)
},
func() {
println(`Did not find an uppercase`)
},
)
}
Find last match...
package main
import (
. "github.com/go-andiamo/streams"
"strings"
)
func main() {
s := Of("a", "B", "c", "D", "e", "F")
upperPredicate := NewPredicate(func(v string) bool {
return strings.ToUpper(v) == v
})
o := s.LastMatch(upperPredicate.Negate())
o.IfPresentOtherwise(
func(v string) {
println(`Found: "` + v + `"`)
},
func() {
println(`Did not find a lowercase`)
},
)
}
Nth match...
package main
import (
"fmt"
. "github.com/go-andiamo/streams"
"strings"
)
func main() {
s := Of("a", "B", "c", "D", "e", "F", "g", "H", "i", "J")
upperPredicate := NewPredicate(func(v string) bool {
return strings.ToUpper(v) == v
})
for nth := -6; nth < 7; nth++ {
s.NthMatch(upperPredicate, nth).IfPresentOtherwise(
func(v string) {
fmt.Printf("Found \"%s\" at nth pos %d\n", v, nth)
},
func() {
fmt.Printf("No match at nth pos %d\n", nth)
},
)
}
}
Sort descending & print...
package main
import (
. "github.com/go-andiamo/streams"
)
func main() {
s := Of("311", "AAAA", "30", "3", "1", "Baaa", "4000", "0400", "40", "Aaaa", "BBBB", "4", "01", "2", "0101", "201", "20")
_ = s.Sorted(StringComparator.Reversed()).ForEach(NewConsumer(func(v string) error {
println(v)
return nil
}))
}
Compound sort...
package main
import (
"fmt"
. "github.com/go-andiamo/streams"
)
func main() {
type myStruct struct {
value int
priority int
}
myComparator := NewComparator(func(v1, v2 myStruct) int {
return IntComparator.Compare(v1.value, v2.value)
}).Then(NewComparator(func(v1, v2 myStruct) int {
return IntComparator.Compare(v1.priority, v2.priority)
}).Reversed())
s := Of(
myStruct{
value: 2,
priority: 2,
},
myStruct{
value: 2,
priority: 0,
},
myStruct{
value: 2,
priority: 1,
},
myStruct{
value: 1,
priority: 2,
},
myStruct{
value: 1,
priority: 1,
},
myStruct{
value: 1,
priority: 0,
},
)
_ = s.Sorted(myComparator).ForEach(NewConsumer(func(v myStruct) error {
fmt.Printf("Value: %d, Priority: %d\n", v.value, v.priority)
return nil
}))
}
Min and max...
package main
import (
"fmt"
. "github.com/go-andiamo/streams"
)
func main() {
type myStruct struct {
value int
priority int
}
myComparator := NewComparator(func(v1, v2 myStruct) int {
return IntComparator.Compare(v1.value, v2.value)
}).Then(NewComparator(func(v1, v2 myStruct) int {
return IntComparator.Compare(v1.priority, v2.priority)
}))
s := Of(
myStruct{
value: 2,
priority: 2,
},
myStruct{
value: 2,
priority: 0,
},
myStruct{
value: 2,
priority: 1,
},
myStruct{
value: 1,
priority: 2,
},
myStruct{
value: 1,
priority: 1,
},
myStruct{
value: 1,
priority: 0,
},
)
min := s.Min(myComparator)
min.IfPresentOtherwise(
func(v myStruct) {
fmt.Printf("Min... Value: %d, Priority: %d\n", v.value, v.priority)
},
func() {
println("No min found!")
})
max := s.Max(myComparator)
max.IfPresentOtherwise(
func(v myStruct) {
fmt.Printf("Max... Value: %d, Priority: %d\n", v.value, v.priority)
},
func() {
println("No max found!")
})
}
Set intersection, union, difference and symmetric difference...
package main
import (
. "github.com/go-andiamo/streams"
)
func main() {
s1 := Of("a", "B", "c", "C", "d", "D", "d")
s2 := Of("e", "E", "a", "A", "b")
println("Intersection...")
_ = s1.Unique(StringInsensitiveComparator).Intersection(s2.Unique(StringInsensitiveComparator), StringInsensitiveComparator).
ForEach(NewConsumer(func(v string) error {
println(v)
return nil
}))
println("Union...")
_ = s1.Unique(StringInsensitiveComparator).Union(s2.Unique(StringInsensitiveComparator), StringInsensitiveComparator).
ForEach(NewConsumer(func(v string) error {
println(v)
return nil
}))
println("Symmetric Difference...")
_ = s1.Unique(StringInsensitiveComparator).SymmetricDifference(s2.Unique(StringInsensitiveComparator), StringInsensitiveComparator).
ForEach(NewConsumer(func(v string) error {
println(v)
return nil
}))
println("Difference (s1 to s2)...")
_ = s1.Unique(StringInsensitiveComparator).Difference(s2.Unique(StringInsensitiveComparator), StringInsensitiveComparator).
ForEach(NewConsumer(func(v string) error {
println(v)
return nil
}))
println("Difference (s2 to s1)...")
_ = s2.Unique(StringInsensitiveComparator).Difference(s1.Unique(StringInsensitiveComparator), StringInsensitiveComparator).
ForEach(NewConsumer(func(v string) error {
println(v)
return nil
}))
}
Map...
package main
import (
. "github.com/go-andiamo/streams"
)
func main() {
type character struct {
name string
age int
}
characters := OfSlice([]character{
{
`Frodo Baggins`,
50,
},
{
`Samwise Gamgee`,
38,
},
{
`Gandalf`,
2000,
},
{
`Aragorn`,
87,
},
{
`Legolas`,
200,
},
{
`Gimli`,
139,
},
{
`Meridoc Brandybuck`,
36,
},
{
`Peregrin Took`,
28,
},
{
`Boromir`,
40,
},
})
m := NewMapper(NewConverter[character, string](func(v character) (string, error) {
return v.name, nil
}))
names, _ := m.Map(characters)
_ = names.Sorted(StringComparator).ForEach(NewConsumer(func(v string) error {
println(v)
return nil
}))
}
Reduce...
package main
import (
"fmt"
. "github.com/go-andiamo/streams"
)
func main() {
type account struct {
currency string
acNo string
balance float64
}
accounts := OfSlice([]account{
{
`GBP`,
`1051065`,
50.01,
},
{
`USD`,
`1931132`,
259.98,
},
{
`EUR`,
`1567807`,
313.25,
},
{
`EUR`,
`1009321`,
50.01,
},
{
`USD`,
`1573756`,
12.02,
},
{
`GBP`,
`1456044`,
99.99,
},
})
accum := NewAccumulator[account, map[string]float64](func(v account, r map[string]float64) map[string]float64 {
if r == nil {
r = map[string]float64{}
}
if cv, ok := r[v.currency]; ok {
r[v.currency] = cv + v.balance
} else {
r[v.currency] = v.balance
}
return r
})
r := NewReducer(accum)
for k, v := range r.Reduce(accounts) {
fmt.Printf("%s %f\n", k, v)
}
}
Filter with composed predicate...
package main
import (
. "github.com/go-andiamo/streams"
"regexp"
"strings"
)
func main() {
s := Of("aaa", "", "AAA", "012", "bBbB", "Ccc", "CCC", "D", "EeE", "eee", " ", " ", "A12")
pNotEmpty := NewPredicate(func(v string) bool {
return len(strings.Trim(v, " ")) > 0
})
rxNum := regexp.MustCompile(`^[0-9]+$`)
pNumeric := NewPredicate(func(v string) bool {
return rxNum.MatchString(v)
})
rxUpper := regexp.MustCompile(`^[A-Z]+$`)
pAllUpper := NewPredicate(func(v string) bool {
return rxUpper.MatchString(v)
})
rxLower := regexp.MustCompile(`^[a-z]+$`)
pAllLower := NewPredicate(func(v string) bool {
return rxLower.MatchString(v)
})
// only want strings that are non-empty and all numeric, all upper or all lower...
pFinal := pNotEmpty.And(pNumeric.Or(pAllUpper).Or(pAllLower))
_ = s.Filter(pFinal).ForEach(NewConsumer(func(v string) error {
println(v)
return nil
}))
}
Distinct vs Unique...
package main
import (
. "github.com/go-andiamo/streams"
)
func main() {
s := Of("a", "A", "b", "B", "c", "C")
println("Distinct...")
_ = s.Distinct().ForEach(NewConsumer(func(v string) error {
println(v)
return nil
}))
println("Unique (case insensitive)...")
_ = s.Unique(StringInsensitiveComparator).ForEach(NewConsumer(func(v string) error {
println(v)
return nil
}))
}
Distinct vs Unique (structs)...
package main
import (
"fmt"
. "github.com/go-andiamo/streams"
"strings"
)
type MyStruct struct {
value string
priority int
}
func main() {
s1 := OfSlice([]MyStruct{
{
"A",
1,
},
{
"A",
2,
},
{
"A",
1,
},
{
"A",
2,
},
})
println("\nStruct Distinct...")
_ = s1.Distinct().ForEach(NewConsumer(func(v MyStruct) error {
fmt.Printf("Value: %s, Priority: %d\n", v.value, v.priority)
return nil
}))
println("\nStruct Unique (no comparator)...")
_ = s1.Unique(nil).ForEach(NewConsumer(func(v MyStruct) error {
fmt.Printf("Value: %s, Priority: %d\n", v.value, v.priority)
return nil
}))
s2 := OfSlice([]*MyStruct{
{
"A",
1,
},
{
"A",
2,
},
{
"A",
1,
},
{
"A",
2,
},
})
println("\nStruct Ptr Distinct...")
_ = s2.Distinct().ForEach(NewConsumer(func(v *MyStruct) error {
fmt.Printf("Value: %s, Priority: %d\n", v.value, v.priority)
return nil
}))
println("\nStruct Ptr Unique (no comparator)...")
_ = s2.Unique(nil).ForEach(NewConsumer(func(v *MyStruct) error {
fmt.Printf("Value: %s, Priority: %d\n", v.value, v.priority)
return nil
}))
cmp := NewComparator(func(v1, v2 *MyStruct) int {
return strings.Compare(v1.value, v2.value)
}).Then(NewComparator(func(v1, v2 *MyStruct) int {
return IntComparator.Compare(v1.priority, v2.priority)
}))
println("\nStruct Ptr Unique (with comparator)...")
_ = s2.Unique(cmp).ForEach(NewConsumer(func(v *MyStruct) error {
fmt.Printf("Value: %s, Priority: %d\n", v.value, v.priority)
return nil
}))
}
For each...
package main
import (
. "github.com/go-andiamo/streams"
)
var stringValuePrinter = NewConsumer(func(v string) error {
println(v)
return nil
})
func main() {
s := Of("a", "b", "c", "d")
_ = s.ForEach(stringValuePrinter)
}
Iterator...
package main
import (
. "github.com/go-andiamo/streams"
)
func main() {
s := Of("a", "b", "c", "d")
next := s.Iterator()
for v, ok := next(); ok; v, ok = next() {
println(v)
}
}
Iterator with predicate...
package main
import (
. "github.com/go-andiamo/streams"
"strings"
)
func main() {
s := Of("a", "B", "c", "D", "e", "F", "g", "H", "i", "J")
upper := NewPredicate(func(v string) bool {
return strings.ToUpper(v) == v
})
next := s.Iterator(upper)
for v, ok := next(); ok; v, ok = next() {
println(v)
}
}