Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/go-andiamo/streams
Golang implementation of streams
https://github.com/go-andiamo/streams
difference generics go golang intersection stream streams symmetric-difference union
Last synced: about 9 hours ago
JSON representation
Golang implementation of streams
- Host: GitHub
- URL: https://github.com/go-andiamo/streams
- Owner: go-andiamo
- License: apache-2.0
- Created: 2022-11-20T14:43:18.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2023-04-29T10:51:54.000Z (over 1 year ago)
- Last Synced: 2024-11-17T02:07:11.178Z (2 months ago)
- Topics: difference, generics, go, golang, intersection, stream, streams, symmetric-difference, union
- Language: Go
- Homepage:
- Size: 132 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Streams
[![GoDoc](https://godoc.org/github.com/go-andiamo/streams?status.svg)](https://pkg.go.dev/github.com/go-andiamo/streams)
[![Latest Version](https://img.shields.io/github/v/tag/go-andiamo/streams.svg?sort=semver&style=flat&label=version&color=blue)](https://github.com/go-andiamo/streams/releases)
[![codecov](https://codecov.io/gh/go-andiamo/streams/branch/main/graph/badge.svg?token=igjnZdgh0e)](https://codecov.io/gh/go-andiamo/streams)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-andiamo/streams)](https://goreportcard.com/report/github.com/go-andiamo/streams)A very light Streams implementation in Golang
## Installation
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
## Interfaces
Stream Interface
Method and description
Returns
AllMatch(p Predicate[T])
returns whether all elements of this stream match the provided predicate
if the provided predicate is nil or the stream is empty, always returns false
bool
AnyMatch(p Predicate[T])
returns whether any elements of this stream match the provided predicate
if the provided predicate is nil or the stream is empty, always returns false
bool
Append(items ...T)
creates a new stream with all the elements of this stream followed by the specified elements
Stream[T]
AsSlice()
returns the underlying slice
[]T
Concat(add Stream[T])
creates a new stream with all the elements of this stream followed by all the elements of the added stream
Stream[T]
Count(p Predicate[T])
returns the count of elements that match the provided predicate
If the predicate is nil, returns the count of all elements
int
Difference(other Stream[T], c Comparator[T])
creates a new stream that is the set difference between this and the supplied other stream
equality of elements is determined using the provided comparator (if the provided comparator is nil, the result is always empty)
Stream[T]
Distinct()
creates a new stream of distinct elements in this stream
Stream[T]
Filter(p Predicate[T])
creates a new stream of elements in this stream that match the provided predicate
if the provided predicate is nil, all elements in this stream are returned
Stream[T]
FirstMatch(p Predicate[T])
returns an optional of the first element that matches the provided predicate<
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])
performs an action on each element of this stream
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])
returns whether this stream contains an element that is equal to the element value provided
equality is determined using the provided comparator
if the provided comparator is nil, always returns false
bool
Intersection(other Stream[T], c Comparator[T])
creates a new stream that is the set intersection of this and the supplied other stream
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])
returns an iterator (pull) function
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])
returns an optional of the last element that matches the provided predicate
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()
returns the length (number of elements) of this stream
int
Limit(maxSize int)
creates a new stream whose number of elements is limited to the value provided
if the maximum size is greater than the length of this stream, all elements are returned
Stream[T]
Max(c Comparator[T])
returns the maximum element of this stream according to the provided comparator
if the provided comparator is nil or the stream is empty, an empty (not present) optional is returned
*Optional[T]
Min(c Comparator[T])
returns the minimum element of this stream according to the provided comparator
if the provided comparator is nil or the stream is empty, an empty (not present) optional is returned
*Optional[T]
MinMax(c Comparator[T])
returns the minimum and maximum element of this stream according to the provided comparator
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])
returns whether none of the elements of this stream match the provided predicate
if the provided predicate is nil or the stream is empty, always returns true
bool
NthMatch(p Predicate[T], nth int)
returns an optional of the nth matching element (1 based) according to the provided predicate
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()
creates a new stream composed of elements from this stream but in reverse order
Stream[T]
Skip(n int)
creates a new stream consisting of this stream after discarding the first n elements
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)
creates a new stream composed of elements from this stream starting at the specified start and including the specified count (or to the end)
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])
creates a new stream consisting of the elements of this stream, sorted according to the provided comparator
if the provided comparator is nil, the elements are not sorted
Stream[T]
SymmetricDifference(other Stream[T], c Comparator[T])
creates a new stream that is the set symmetric difference between this and the supplied other stream
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])
creates a new stream that is the set union of this and the supplied other stream
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])
creates a new stream of unique elements in this stream
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]
creates a new stream of the values provided
OfSlice[T any](s []T) Stream[T]
creates a new stream around a slice
Note: Once created, If the slice changes the stream does not
NewStreamableSlice[T any](sl *[]T) Stream[T]
creates a Stream from a pointer to a slice
It differs from casting a slice to Streamable in that if the underlying slice changes, so does the Stream
Casting as Streamable
sl := []string{"a", "b", "c"}
s := Streamable[string](sl)
casts a slice as a
Stream
Note: Once cast, if the slice changes the stream does not
Comparator Interface
Method and description
Returns
Compare(v1, v2 T)
- the result should be 0 if v1 == v2
- the result should be -1 if v1 < v2
- the result should be 1 if v1 > v2
compares the two values lexicographically, i.e.:
int
Greater(v1, v2 T)
returns true if v1 > v2, otherwise false
bool
GreaterOrEqual(v1, v2 T)
returns true if v1 >= v2, otherwise false
bool
Equal(v1, v2 T)
returns true if v1 == v2, otherwise false
bool
Less(v1, v2 T)
returns true if v1 < v2, otherwise false
bool
LessOrEqual(v1, v2 T)
returns true if v1 <= v2, otherwise false
bool
NotEqual(v1, v2 T)
returns true if v1 != v2, otherwise false
bool
Reversed()
creates a new comparator that imposes the reverse ordering to this comparator
the reversal is against less/greater as well as against equality/non-equality
Comparator[T]
Then(other Comparator[T])
creates a new comparator from this comparator, with a following then comparator
that is used when the initial comparison yields equal
Comparator[T]
Constructors
NewComparator[T any](f ComparatorFunc[T]) Comparator[T]
-
0 if v1 == v2 -
-1 if v1 < v2 -
1 if v1 > v2
creates a new
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)
is called by the user of the consumer to supply a value
error
AndThen(after Consumer[T])
creates a new consumer from the current with a subsequent action to be performed
multiple consumers can be chained together as one using this method
Consumer[T]
Constructors
NewConsumer[T any](f ConsumerFunc[T]) Consumer[T]
creates a new
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)
evaluates this predicate against the supplied value
bool
And(other Predicate[T])
creates a composed predicate that represents a short-circuiting logical AND of this predicate and another
Predicate[T]
Or(other Predicate[T])
creates a composed predicate that represents a short-circuiting logical OR of this predicate and another
Predicate[T]
Negate()
creates a composed predicate that represents a logical NOT of this predicate
Predicate[T]
Constructors
NewPredicate[T any](f PredicateFunc[T]) Predicate[T]
creates a new
Predicate
from the function providedwhere the predicate function is:
type PredicateFunc[T any] func(v T) bool
### Mapper Interfaces
Mapper Interface
Method and description
Returns
Map(in Stream[T])
converts the values in the input
Stream
and produces a Stream
of output types
(Stream[R], error)
Constructors
NewMapper[T any, R any](c Converter[T, R]) Mapper[T, R]
creates a new
Mapper
that will use the provided Converter
NewMapper
panics if a nil Converter
is supplied
Converter Interface
Method and description
Returns
Convert(v T)
converts a value of type T and returns a value of type R
(R, error)
Constructors
NewConverter[T any, R any](f ConverterFunc[T, R]) Converter[T, R]
creates a new
Converter
from the function providedwhere the converter function is:
type ConverterFunc[T any, R any] func(v T) (R, error)
### Reducer Interfaces
Reducer Interface
Method and description
Returns
Reduce(s Stream[T])
performs a reduction of the supplied
Stream
R
Constructors
NewReducer[T any, R any](accumulator Accumulator[T, R]) Reducer[T, R]
creates a new
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)
adds the value of T to R, and returns the new R
R
Constructors
NewAccumulator[T any, R any](f AccumulatorFunc[T, R]) Accumulator[T, R]
creates a new
Accumulator
from the function providedwhere the accumulator function is:
type AccumulatorFunc[T any, R any] func(t T, r R) R
## Examples
Find first match...
```go
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`)
},
)
}
```
[try on go-playground](https://go.dev/play/p/C-GYuInfkNm)
Find last match...
```go
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`)
},
)
}
```
[try on go-playground](https://go.dev/play/p/2UcwpZEuV-L)
Nth match...
```go
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)
},
)
}
}
```
[try on go-playground](https://go.dev/play/p/I_DrKK4ZXAT)
Sort descending & print...
```go
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
}))
}
```
[try on go-playground](https://go.dev/play/p/bU6UZ479pF1)
Compound sort...
```go
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
}))
}
```
[try on go-playground](https://go.dev/play/p/TYeVcgjdAB3)
Min and max...
```go
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!")
})
}
```
[try on go-playground](https://go.dev/play/p/7uUKh5Qg-7L)
Set intersection, union, difference and symmetric difference...
```go
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
}))
}
```
[try on go-playground](https://go.dev/play/p/IMpym38xHXV)
Map...
```go
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
}))
}
```
[try on go-playground](https://go.dev/play/p/oe5ZTSbzUyy)
Reduce...
```go
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)
}
}
```
[try on go-playground](https://go.dev/play/p/HwgwlkNFUTQ)
Filter with composed predicate...
```go
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
}))
}
```
[try on go-playground](https://go.dev/play/p/WxOOpEv-kI0)
Distinct vs Unique...
```go
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
}))
}
```
[try on go-playground](https://go.dev/play/p/JZY9b6o6OLd)
Distinct vs Unique (structs)...
```go
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
}))
}
```
[try on go-playground](https://go.dev/play/p/9Caw_RT4hwp)
For each...
```go
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)
}
```
[try on go-playground](https://go.dev/play/p/yks2s2E8czr)
Iterator...
```go
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)
}
}
```
[try on go-playground](https://go.dev/play/p/Yae6ZLi2vVj)
Iterator with predicate...
```go
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)
}
}
```
[try on go-playground](https://go.dev/play/p/GDQJDsZsSY9)