Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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

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)


    compares the two values lexicographically, i.e.:

    • the result should be 0 if v1 == v2

    • the result should be -1 if v1 < v2

    • the result should be 1 if v1 > v2





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]


    creates a new Comparator from the function provided

    where the comparator function is:

    type ComparatorFunc[T any] func(v1, v2 T) int

    which returns:


    • 0 if v1 == v2


    • -1 if v1 < v2


    • 1 if v1 > v2





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 provided

    where 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 provided

    where 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 provided

    where 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 provided

    where 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)