Skip to content

Golang implementation of generic lazily iterated sequences.

License

Notifications You must be signed in to change notification settings

orangootan/go-seq

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

License: MIT Go Version Release GoDoc Go Report Card CodeFactor Test Coverage Status codecov

go-seq

Golang implementation of generic lazily iterated sequences.
Requires Go 1.18+.

Package

import "github.com/orangootan/go-seq/pkg/seq"

All examples use unqualified import as if:

import . "github.com/orangootan/go-seq/pkg/seq"

Examples

Seq[T]

Seq[T] is the main interface declaring methods for working with sequences. Some functions are implemented as standalone functions (not methods of this interface) due to limitations of Go generics implementation.

FromSlice

FromSlice() returns a sequence of items from the given slice.
See further examples.

Iterator

Iterator() returns iterator object of a sequence.
Next() advances iterator to the next item of the iterated sequence. Returns true if successfully advanced or false if passed the end of the sequence.
Current() returns pointer to the current item of the iterated sequence.

s := []int{1, 2, 3}
it := FromSlice(s).Iterator()
for it.Next() {
    fmt.Println(*it.Current())
}
// Output:
// 1
// 2
// 3

AsSlice

AsSlice() collects all sequence items into newly created slice.
See further examples.

FromSliceReversed

FromSliceReversed() returns a sequence of items from the given slice in inverted order.

s := FromSliceReversed([]int{1, 2, 3, 4})
fmt.Println(s.AsSlice())
// Output:
// [4 3 2 1]

FromChan

FromChan() returns a sequence of items received from a channel until the channel is closed.

ch := make(chan int)
go func() {
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)
}()
fmt.Println(FromChan(ch).AsSlice())
// Output:
// [1 2 3]

Generate

Generate() returns an infinite sequence applying a mapping function to item indices.

s := Generate(func(i int) int { return i * i })
fmt.Println(s.Take(6).AsSlice())
// Output:
// [0 1 4 9 16 25]

Iterate

Iterate() returns an infinite sequence consisting of seed, fn(seed), fn(fn(seed)), etc.

s := Iterate(2, func(i int) int { return 2 * i })
fmt.Println(s.Take(8).AsSlice())
// Output:
// [2 4 8 16 32 64 128 256]

Cycle

Cycle() returns an infinite sequence by cycling items from the given slice.

s := []int{1, 2, 3}
r := Cycle(s).Take(9)
fmt.Println(r.AsSlice())
// Output:
// [1 2 3 1 2 3 1 2 3]

Repeat

Repeat() returns an infinite sequence repeating the given item.

s := Repeat(1).Take(5)
fmt.Println(s.AsSlice())
// Output:
// [1 1 1 1 1]

Single

// Single returns a sequence containing exactly one specified item.

s := Single(3)
fmt.Println(s.AsSlice())
fmt.Println(s.Count())
// Output:
// [3]
// 1

Empty

Empty() returns empty sequence.

fmt.Println(Empty[int]().Count())
fmt.Println(Empty[string]().AsSlice())
fmt.Println(Empty[int]().IsEmpty())
it := Empty[int]().Iterator()
fmt.Println(it.Next(), it.Current())
// Output:
// 0
// []
// true
// false <nil>

IsEmpty

IsEmpty() determines whether a sequence is empty.

s1 := []int{1, 2, 3}
var s2 []int
fmt.Println(FromSlice(s1).IsEmpty())
fmt.Println(FromSlice(s2).IsEmpty())
fmt.Println(Empty[int]().IsEmpty())
// Output:
// false
// true
// true

Equal

Equal() determines whether two sequences have equal length and equal corresponding items. This function works only with sequences of comparable items.

s1 := FromSlice([]int{1, 2, 3, 4})
s2 := FromSlice([]int{1, 2, 3})
s3 := FromSlice([]int{0, 1, 2})
s4 := FromSlice([]int{0, 1, 2})
fmt.Println(Equal(s1, s2))
fmt.Println(Equal(s2, s3))
fmt.Println(Equal(s3, s4))
// Output:
// false
// false
// true

Concat

Concat() concatenates two sequences.

s1 := FromSlice([]int{1, 2, 3, 4})
s2 := FromSlice([]int{5, 6, 7})
r := s1.Concat(s2)
fmt.Println(r.AsSlice())
// Output:
// [1 2 3 4 5 6 7]

Cycled

Cycled() repeats a sequence infinitely.

s := []int{1, 2, 3}
r := FromSlice(s).Cycled().Take(10)
fmt.Println(r.AsSlice())
// Output:
// [1 2 3 1 2 3 1 2 3 1]

Filter (Where)

Filter() filters a sequence of items based on the given predicate.
Where() is an alias for Filter(). Use whatever name you like.

isEven := func(i int) bool { return i%2 == 0 }
s := []int{1, 2, 3, 4, 5, 6}
r := FromSlice(s).Filter(isEven)
fmt.Println(r.AsSlice())
// Output:
// [2 4 6]

Reverse

Reverse() inverts the order of items in a sequence.

s := []int{1, 2, 3, 4, 5, 6}
r := FromSlice(s).Reverse()
fmt.Println(r.AsSlice())
// Output:
// [6 5 4 3 2 1]

Skip

Skip() bypasses the given number of items in a sequence and then returns the remaining items.

s := []int{1, 2, 3, 4, 5, 6}
r := FromSlice(s).Skip(3)
fmt.Println(r.AsSlice())
// Output:
// [4 5 6]

SkipWhile

SkipWhile() bypasses items in a sequence as long as the given condition is true and then returns the remaining items.

lessThan5 := func(i int) bool { return i < 5 }
s1 := []int{1, 2, 3, 4, 5, 6, 1}
s2 := []int{1, 2, 3}
r1 := FromSlice(s1).SkipWhile(lessThan5)
r2 := FromSlice(s2).SkipWhile(lessThan5)
fmt.Println(r1.AsSlice())
fmt.Println(r2.AsSlice())
// Output:
// [5 6 1]
// []

Take

Take() returns the given number of items from the start of a sequence.

s := []int{1, 2, 3, 4, 5, 6}
r := FromSlice(s).Take(4)
fmt.Println(r.AsSlice())
// Output:
// [1 2 3 4]

TakeWhile

TakeWhile() returns items from a sequence as long as the given condition is true, and then skips the remaining items.

lessThan5 := func(i int) bool { return i < 5 }
s := []int{1, 2, 3, 4, 5, 6, 1}
r := FromSlice(s).TakeWhile(lessThan5)
fmt.Println(r.AsSlice())
// Output:
// [1 2 3 4]

Interleave

Interleave() returns a sequence that interleaves items from two sequences.

s1 := FromSlice([]int{1, 2, 3, 4})
s2 := FromSlice([]int{4, 5, 6})
r := s1.Interleave(s2)
fmt.Println(r.AsSlice())
// Output:
// [1 4 2 5 3 6 4]

Map (Select)

Map() projects items of a sequence using the given function.
Select() is an alias for Map(). Use whatever name you like.

s := FromSlice([]int{1, 2, 3, 4, 5, 6})
r := Map(s, func(i int) int { return i * i })
fmt.Println(r.AsSlice())
// Output:
// [1 4 9 16 25 36]

Zip

Zip() combines items of two sequences pairwise using a projection function.

s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6, 7})
r := Zip(s1, s2, func(u int, v int) [2]int {
    return [2]int{u, v}
})
fmt.Println(r.AsSlice())
// Output:
// [[1 4] [2 5] [3 6]]

Zip3

Zip3() is same as Zip but combines three sequences.

s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6})
s3 := FromSlice([]int{7, 8, 9, 10})
r := Zip3(s1, s2, s3, func(u int, v int, w int) [3]int {
    return [3]int{u, v, w}
})
fmt.Println(r.AsSlice())
// Output:
// [[1 4 7] [2 5 8] [3 6 9]]

All

All() determines whether all items of a sequence satisfy the given condition.

isEven := func(i int) bool { return i%2 == 0 }
s1 := []int{2, 4, 6, 8, 10}
s2 := []int{2, 4, 6, 8, 11}
fmt.Println(FromSlice(s1).All(isEven))
fmt.Println(FromSlice(s2).All(isEven))
// Output:
// true
// false

Any

Any() determines whether any item of a sequence satisfies the given condition.

isEven := func(i int) bool { return i%2 == 0 }
s1 := []int{1, 3, 5, 7, 10}
s2 := []int{1, 3, 5, 7, 9}
fmt.Println(FromSlice(s1).Any(isEven))
fmt.Println(FromSlice(s2).Any(isEven))
// Output:
// true
// false

Chunk

Chunk() splits a sequence into chunks of the given size and returns sequence of slices.

s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
r := Chunk(3, FromSlice(s))
fmt.Println(r.AsSlice())
// Output:
// [[1 2 3] [4 5 6] [7 8 9] [10 11]]

Contains

Contains() determines whether a sequence contains the given item. This function works only with sequences of comparable items.

s := FromSlice([]int{1, 2, 3, 4, 5})
fmt.Println(Contains(3, s))
fmt.Println(Contains(6, s))
// Output:
// true
// false

Count (method)

Count() counts number of items in a sequence.

s1 := []int{1, 2, 3, 4, 5}
var s2 []int
fmt.Println(FromSlice(s1).Count())
fmt.Println(FromSlice(s2).Count())
// Output:
// 5
// 0

Count (function)

Count() counts number of items in a sequence. You can specify an integer type for result.

s := []string{"Huey", "Dewey", "Louie"}
c := Count[string, uint64](FromSlice(s))
fmt.Printf("%T %v", c, c)
// Output:
// uint64 3

Distinct

Distinct() returns distinct elements from a sequence. This function works only with sequences of comparable items.

s := []int{2, 2, 3, 1, 2, 3}
r := Distinct(FromSlice(s))
fmt.Println(r.AsSlice())
// Output:
// [2 3 1]

First

First() returns undefined value and false if a sequence is empty or the first item and true otherwise.

s1 := []int{1, 2, 3}
var s2 []int
fmt.Println(FromSlice(s1).First())
fmt.Println(FromSlice(s2).First())
// Output:
// 1 true
// 0 false

FirstOrDefault

FirstOrDefault() returns default value of type T if a sequence is empty or the first item otherwise.

s1 := []int{1, 2, 3}
var s2 []int
fmt.Println(FromSlice(s1).FirstOrDefault())
fmt.Println(FromSlice(s2).FirstOrDefault())
// Output:
// 1
// 0

Flatten

Flatten() combines a sequence of item sequences into flat sequence of items.

s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6})
s3 := FromSlice([]int{7, 8, 9})
ss := FromSlice([]Seq[int]{s1, s2, s3})
fmt.Println(Flatten(ss).AsSlice())
// Output:
// [1 2 3 4 5 6 7 8 9]

FlatMap

FlatMap() maps each sequence item to a sequence and flattens results.

s := []int{1, 2, 3}
r := FlatMap(FromSlice(s), func(i int) Seq[int] {
    return Repeat(i).Take(i)
})
fmt.Println(r.AsSlice())
// Output:
// [1 2 2 3 3 3]

Reduce

Reduce() applies an accumulator function over a sequence given a specified initial value.

sum := func(a int, b int) int { return a + b }
s1 := []int{1, 2, 3, 4, 5}
var s2 []int
fmt.Println(FromSlice(s1).Reduce(0, sum))
fmt.Println(FromSlice(s2).Reduce(0, sum))
// Output:
// 15
// 0

Fold

Fold() applies an accumulator function over a sequence given a specified initial value. Accumulator and item types can be different.

sum := func(a int, b int) int { return a + b }
appendInt := func(s []int, i int) []int {
    return append(s, i)
}
s := FromSlice([]int{1, 2, 3})
fmt.Println(Fold(s, 0, sum))
fmt.Println(Fold(s, []int{4, 5}, appendInt))
// Output:
// 6
// [4 5 1 2 3]

ForEach

ForEach() performs an action for each item in a sequence.

s := []string{"Huey", "Dewey", "Louie"}
FromSlice(s).ForEach(func(name string) {
    fmt.Println(name)
})
// Output:
// Huey
// Dewey
// Louie

ForEnumerated

ForEnumerated() is similar to ForEach but passes item index as first parameter of action function.

s := []string{"Huey", "Dewey", "Louie"}
FromSlice(s).ForEnumerated(func(i int, name string) {
    fmt.Println(i, name)
})
// Output:
// 0 Huey
// 1 Dewey
// 2 Louie

GroupBy

GroupBy() returns a map grouping sequence items by key produced by the given function. Key type must be comparable.

isEven := func(i int) bool { return i%2 == 0 }
s := []int{1, 2, 3, 4, 5, 6}
g := GroupBy(FromSlice(s), isEven)
for k, v := range g {
    fmt.Printf("%v: %v\n", k, v)
}
// Unordered output:
// false: [1 3 5]
// true: [2 4 6]

Inspect

Inspect() allows to observe items of a sequence by performing an action for each item.

isEven := func(i int) bool { return i%2 == 0 }
FromSlice([]int{1, 2, 3}).
    Inspect(func(i int) {
        fmt.Printf("before: %v\n", i)
    }).
    Filter(isEven).
    Inspect(func(i int) {
        fmt.Printf("after: %v\n", i)
    }).
    Count()
// Output:
// before: 1
// before: 2
// after: 2
// before: 3

Partition

Partition() returns two slices. The first one contains items satisfying the given condition, the second one contains the rest of them.

s := FromSlice([]int{1, 2, 3, 4, 5, 6})
t, f := s.Partition(func(i int) bool { return i%2 == 0 })
fmt.Println(t)
fmt.Println(f)
// Output:
// [2 4 6]
// [1 3 5]

Sum

Sum() returns sum of elements in a sequence. This function works with sequences of numbers.

s1 := []int{1, 2, 3, 4}
var s2 []int
fmt.Println(Sum(FromSlice(s1)))
fmt.Println(Sum(FromSlice(s2)))
// Output:
// 10
// 0

Product

Product() returns product of elements in a sequence. This function works with sequences of numbers.

s1 := []int{1, 2, 3, 4}
var s2 []int
fmt.Println(Product(FromSlice(s1)))
fmt.Println(Product(FromSlice(s2)))
// Output:
// 24
// 1

Average

Average() returns average number of a sequence of numbers. This function works with sequences of integer and float numbers.

s1 := []int{1, 2, 3, 4, 5}
fmt.Println(Average(FromSlice(s1)))
// Output:
// 3

Max

Max() returns the maximum element of a sequence and true if the sequence is not empty or undefined value and false otherwise. This function works with sequences of integer numbers, float numbers and strings.

s1 := []int{1, 2, 3}
s2 := []string{"Huey", "Dewey", "Louie"}
var s3 []int
fmt.Println(Max(FromSlice(s1)))
fmt.Println(Max(FromSlice(s2)))
fmt.Println(Max(FromSlice(s3)))
// Output:
// 3 true
// Louie true
// 0 false

Min

Min() returns the minimum element of a sequence and true if the sequence is not empty or undefined value and false otherwise. This function works with sequences of integer numbers, float numbers and strings.

s1 := []int{1, 2, 3}
s2 := []string{"Huey", "Dewey", "Louie"}
var s3 []int
fmt.Println(Min(FromSlice(s1)))
fmt.Println(Min(FromSlice(s2)))
fmt.Println(Min(FromSlice(s3)))
// Output:
// 1 true
// Dewey true
// 0 false

AsChan

AsChan() creates a channel and sends all sequence items into it in separate goroutine. After the sequence exhausted the channel is closed. You can specify the channel capacity with cap parameter.

ch := FromSlice([]int{1, 2, 3}).AsChan(0)
for i := range ch {
    fmt.Println(i)
}
// Output:
// 1
// 2
// 3

ToChan

ToChan() sends sequence items to an existing channel in current goroutine so this call is blocking. This function does not close the channel after the sequence is exhausted.

ch := make(chan int)
go func() {
    FromSlice([]int{1, 2}).ToChan(ch)
    FromSlice([]int{3, 4}).ToChan(ch)
    close(ch)
}()
for i := range ch {
    fmt.Println(i)
}
// Output:
// 1
// 2
// 3
// 4

ToSlice

ToSlice() appends items of a sequence to an existing slice given by reference.

s1 := []int{1, 2}
s2 := []int{3, 4, 5}
FromSlice(s2).ToSlice(&s1)
fmt.Println(s1)
// Output:
// [1 2 3 4 5]

FilterP (WhereP)

FilterP() filters a sequence of items based on the given predicate.
This is parallel version of Filter() using specified number of goroutines to process items in parallel. WhereP() is an alias for FilterP(). Use whatever name you like.

isEven := func(i int) bool {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    return i%2 == 0
}
s := []int{1, 2, 3, 4, 5, 6}
FromSlice(s).FilterP(2, isEven).ForEach(func(i int) {
    fmt.Println(i)
})
// Unordered output:
// 2
// 4
// 6

MapP (SelectP)

MapP() projects items of a sequence using the given function.
This is parallel version of Map() using specified number of goroutines to process items in parallel. SelectP() is an alias for MapP(). Use whatever name you like.

s := FromSlice([]int{1, 2, 3, 4, 5, 6})
MapP(2, s, func(i int) int {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    return i * i
}).ForEach(func(i int) {
    fmt.Println(i)
})
// Unordered output:
// 1
// 4
// 9
// 16
// 25
// 36

FlatmapP

FlatMapP() maps each sequence item to a sequence and flattens results.
This is parallel version of FlatMap() using specified number of goroutines to process items in parallel.

s := FromSlice([]int{1, 2, 3})
FlatMapP(2, s, func(i int) Seq[int] {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    return Repeat(i).Take(i)
}).ForEach(func(i int) {
    fmt.Println(i)
})
// Unordered output:
// 1
// 2
// 2
// 3
// 3
// 3

ReduceP

ReduceP() applies an accumulator function over a sequence given a specified initial value.
This is parallel version of Reduce() using specified number of goroutines to process items in parallel.

sum := func(a int, b int) int {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    return a + b
}
s1 := []int{1, 2, 3, 4, 5}
var s2 []int
fmt.Println(FromSlice(s1).ReduceP(2, 0, sum))
fmt.Println(FromSlice(s2).ReduceP(2, 0, sum))
// Output:
// 15
// 0

ForEachP

ForEachP() performs an action for each item in a sequence.
This is parallel version of ForEach() using specified number of goroutines to process items in parallel.

s := []string{"Huey", "Dewey", "Louie"}
FromSlice(s).ForEachP(2, func(name string) {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    fmt.Println(name)
})
// Unordered output:
// Huey
// Dewey
// Louie

ForEnumeratedP

ForEnumeratedP() is similar to ForEachP() but passes item index as first parameter of action function.
This is parallel version of ForEnumerated() using specified number of goroutines to process items in parallel.

s := []string{"Huey", "Dewey", "Louie"}
FromSlice(s).ForEnumeratedP(2, func(i int, name string) {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    fmt.Println(i, name)
})
// Unordered output:
// 0 Huey
// 1 Dewey
// 2 Louie

AllP

AllP() determines whether all items of a sequence satisfy the given condition.
This is parallel version of All() using specified number of goroutines to process items in parallel.

isEven := func(i int) bool {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    return i%2 == 0
}
s1 := []int{2, 4, 6, 8, 10, 12, 14, 16}
s2 := []int{2, 4, 6, 8, 11, 12, 14, 16}
fmt.Println(FromSlice(s1).AllP(2, isEven))
fmt.Println(FromSlice(s2).AllP(2, isEven))
// Output:
// true
// false

AnyP

AnyP() determines whether any item of a sequence satisfies the given condition.
This is parallel version of Any() using specified number of goroutines to process items in parallel.

isEven := func(i int) bool {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    return i%2 == 0
}
s1 := []int{1, 3, 5, 7, 9, 11, 13, 15}
s2 := []int{1, 2, 4, 7, 9, 11, 13, 16}
fmt.Println(FromSlice(s1).AnyP(2, isEven))
fmt.Println(FromSlice(s2).AnyP(2, isEven))
// Output:
// false
// true

PartitionP

PartitionP() returns two slices. The first one contains items satisfying the given condition, the second one contains the rest of them.
This is parallel version of Partition() using specified number of goroutines to process items in parallel.

s := FromSlice([]int{1, 2, 3, 4, 5, 6})
t, f := s.PartitionP(1, func(i int) bool {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    return i%2 == 0
})
for _, i := range t {
    fmt.Println("even:", i)
}
for _, i := range f {
    fmt.Println("odd:", i)
}
// Unordered output:
// even: 2
// even: 4
// even: 6
// odd: 1
// odd: 3
// odd: 5

ZipP

ZipP() combines items of two sequences pairwise using a projection function.
This is parallel version of Zip() using specified number of goroutines to process items in parallel.

s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6, 7})
ZipP(2, s1, s2, func(u int, v int) [2]int {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    return [2]int{u, v}
}).ForEach(func(s [2]int) {
    fmt.Println(s)
})
// Unordered output:
// [1 4]
// [2 5]
// [3 6]

Zip3P

Zip3P() is same as ZipP() but combines three sequences.
This is parallel version of Zip3() using specified number of goroutines to process items in parallel.

s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6})
s3 := FromSlice([]int{7, 8, 9, 10})
Zip3P(2, s1, s2, s3, func(u int, v int, w int) [3]int {
    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
    return [3]int{u, v, w}
}).ForEach(func(s [3]int) {
    fmt.Println(s)
})
// Unordered output:
// [1 4 7]
// [2 5 8]
// [3 6 9]